chore: release [ci skip]

This commit is contained in:
gitea-actions
2026-03-31 12:32:56 +00:00
parent 5dd8af91b2
commit 68930cfd9c
7 changed files with 74 additions and 64 deletions
+26 -15
View File
@@ -233,9 +233,9 @@ registration flow. Two-step process: phone verification → account creation.
### 4. Payment Page (app/(main)/payment/page.tsx)
**Route:** `/payment`
**Type:** Client component
**Description:** Payment/checkout page. Shows cart items in a table with quantity controls and a summary aside with payment actions.
**Route:** `/payment` **Type:** Client component **Description:**
Payment/checkout page. Shows cart items in a table with quantity controls and a
summary aside with payment actions.
#### Key Features
@@ -248,32 +248,43 @@ registration flow. Two-step process: phone verification → account creation.
- **Invoice Aside:**
- Sticky on desktop (top offset = header height + 1rem)
- Shows total price
- Payment buttons: Tiền mặt, QR Code (UI-only; trigger review modal for customers)
- **"Đánh giá" button** — only visible when `user.role === "customer"`; opens ReviewModal
- **"Quay về" button** — links back to `/`; spans full width when review button is absent
- Payment buttons: Tiền mặt, QR Code (UI-only; trigger review modal for
customers)
- **"Đánh giá" button** — only visible when `user.role === "customer"`; opens
ReviewModal
- **"Quay về" button** — links back to `/`; spans full width when review
button is absent
- **Review Modal (ReviewModal):**
- Opened when a customer clicks "Đánh giá" OR clicks a payment button (Tiền mặt/QR)
- Closed via "Quay lại" button, backdrop click, or after submitting and closing
- Opened when a customer clicks "Đánh giá" OR clicks a payment button (Tiền
mặt/QR)
- Closed via "Quay lại" button, backdrop click, or after submitting and
closing
- See `components/ReviewModal.tsx` for full documentation
#### Context Usage
- **useCart()** — items, totalPrice, increaseQty, decreaseQty, removeFromCart, setQuantity
- **useAuth()** — user (to check `user.role === "customer"` for review button visibility)
- **useCart()** — items, totalPrice, increaseQty, decreaseQty, removeFromCart,
setQuantity
- **useAuth()** — user (to check `user.role === "customer"` for review button
visibility)
#### State Management
```tsx
isReviewOpen: boolean // Controls ReviewModal visibility
isCustomer: boolean // Derived from user.role === "customer"
isReviewOpen: boolean; // Controls ReviewModal visibility
isCustomer: boolean; // Derived from user.role === "customer"
```
#### Responsive Behavior
- **Mobile:** Single column layout, table scrolls horizontally, button labels hidden (icon only)
- **Desktop (lg+):** Button labels visible, aside becomes sticky sidebar (xl: w-85)
- **Review button grid:** When customer is logged in, 3 buttons in 2-column grid; "Quay về" occupies remaining space. Otherwise 2-column grid with "Quay về" spanning full width.
- **Mobile:** Single column layout, table scrolls horizontally, button labels
hidden (icon only)
- **Desktop (lg+):** Button labels visible, aside becomes sticky sidebar (xl:
w-85)
- **Review button grid:** When customer is logged in, 3 buttons in 2-column
grid; "Quay về" occupies remaining space. Otherwise 2-column grid with "Quay
về" spanning full width.
---
+28 -20
View File
@@ -200,52 +200,60 @@ hover.
## ReviewModal
**File:** components/ReviewModal.tsx
**Description:** Modal for customer reviews. Shows a 5-star rating selector and a textarea for written feedback. After submission, displays a thank-you message. Appears on the payment page for customers (via a dedicated "Đánh giá" button) and also opens automatically after a successful payment action.
**File:** components/ReviewModal.tsx **Description:** Modal for customer
reviews. Shows a 5-star rating selector and a textarea for written feedback.
After submission, displays a thank-you message. Appears on the payment page for
customers (via a dedicated "Đánh giá" button) and also opens automatically after
a successful payment action.
### Props
| Prop | Type | Required | Default | Description |
| ------- | ---------- | -------- | ------- | ------------------------------------------ |
| isOpen | boolean | yes | - | Controls modal visibility |
| onClose | () => void | yes | - | Callback to close the modal & reset state |
| Prop | Type | Required | Default | Description |
| ------- | ---------- | -------- | ------- | ----------------------------------------- |
| isOpen | boolean | yes | - | Controls modal visibility |
| onClose | () => void | yes | - | Callback to close the modal & reset state |
### Behavior
- **Star rating:** 5 interactive stars; hover highlights up to hovered star, click locks selection. Disabled submit button until at least 1 star selected.
- **Star rating:** 5 interactive stars; hover highlights up to hovered star,
click locks selection. Disabled submit button until at least 1 star selected.
- **Star labels:** 1 = Rất tệ, 2 = Tệ, 3 = Bình thường, 4 = Tốt, 5 = Xuất sắc
- **Textarea:** Optional free-text review (placeholder in Vietnamese).
- **Footer buttons:**
- "Quay lại" — closes modal without submitting; resets all state.
- "Xác nhận" — submits (UI-only); transitions to thank-you state.
- **Thank-you state:** Replaces form with "Cảm ơn quý khách" message + close button.
- **Thank-you state:** Replaces form with "Cảm ơn quý khách" message + close
button.
- **Backdrop click:** Also closes the modal (same as "Quay lại").
- **State reset:** All state (rating, hover, review text, submitted) resets on close.
- **State reset:** All state (rating, hover, review text, submitted) resets on
close.
### Styling
| Element | Key classes |
| --------------- | ----------------------------------------------------------------------- |
| Backdrop | fixed inset-0, bg-black/50 backdrop-blur-sm, z-50 |
| Backdrop | fixed inset-0, bg-black/50 backdrop-blur-sm, z-50 |
| Modal panel | max-w-md, rounded-2xl, border --color-border-light, white bg, shadow-xl |
| Stars | text-3xl sm:text-4xl, hover:scale-110, active:scale-95 |
| Stars | text-3xl sm:text-4xl, hover:scale-110, active:scale-95 |
| Active star | fa-solid fa-star text-yellow-400 |
| Inactive star | fa-regular fa-star text-(--color-border) |
| Textarea | rounded-xl, focus:ring with --color-primary/20 |
| Quay lại button | border --color-border, hover --color-border-light |
| Xác nhận button | bg --color-primary, disabled:opacity-50 |
| Thank-you icon | fa-solid fa-heart text-(--color-accent), --color-accent-light bg |
| Inactive star | fa-regular fa-star text-(--color-border) |
| Textarea | rounded-xl, focus:ring with --color-primary/20 |
| Quay lại button | border --color-border, hover --color-border-light |
| Xác nhận button | bg --color-primary, disabled:opacity-50 |
| Thank-you icon | fa-solid fa-heart text-(--color-accent), --color-accent-light bg |
### Responsive
- Padding: `p-6 sm:p-8` — larger on sm+ screens
- Stars: `text-3xl sm:text-4xl`
- Modal width: `w-full max-w-md` with `p-4` page padding — works on all screen sizes
- Modal width: `w-full max-w-md` with `p-4` page padding — works on all screen
sizes
### Dependencies
- React useState
- FontAwesome icons (fa-star, fa-regular fa-star, fa-heart, fa-arrow-left, fa-check)
- FontAwesome icons (fa-star, fa-regular fa-star, fa-heart, fa-arrow-left,
fa-check)
- Tailwind CSS + CSS custom properties from globals.css
### Usage in Payment Page
@@ -255,14 +263,14 @@ hover.
const [isReviewOpen, setIsReviewOpen] = useState(false);
// Button in payment aside (only visible to customers)
<button onClick={() => setIsReviewOpen(true)}>Đánh giá</button>
<button onClick={() => setIsReviewOpen(true)}>Đánh giá</button>;
// Payment buttons (Tiền mặt / QR Code) also open the modal for customers
const handlePayment = () => {
if (isCustomer) setIsReviewOpen(true);
};
<ReviewModal isOpen={isReviewOpen} onClose={() => setIsReviewOpen(false)} />
<ReviewModal isOpen={isReviewOpen} onClose={() => setIsReviewOpen(false)} />;
```
---
+5 -5
View File
@@ -52,7 +52,7 @@ export default function ReviewModal({ isOpen, onClose }: ReviewModalProps) {
</div>
<h2
id="review-modal-title"
className="text-xl font-bold text-foreground"
className="text-foreground text-xl font-bold"
>
Cảm ơn quý khách
</h2>
@@ -71,7 +71,7 @@ export default function ReviewModal({ isOpen, onClose }: ReviewModalProps) {
<>
<h2
id="review-modal-title"
className="mb-1 text-xl font-bold text-foreground"
className="text-foreground mb-1 text-xl font-bold"
>
Đánh giá của bạn
</h2>
@@ -138,7 +138,7 @@ export default function ReviewModal({ isOpen, onClose }: ReviewModalProps) {
onChange={(e) => setReview(e.target.value)}
placeholder="Chia sẻ cảm nhận của bạn về đồ uống, dịch vụ..."
rows={4}
className="w-full resize-none rounded-xl border border-(--color-border) bg-transparent px-3 py-2.5 text-sm text-foreground placeholder:text-(--color-text-muted) focus:border-(--color-primary) focus:outline-none focus:ring-2 focus:ring-(--color-primary)/20 transition-colors"
className="text-foreground w-full resize-none rounded-xl border border-(--color-border) bg-transparent px-3 py-2.5 text-sm transition-colors placeholder:text-(--color-text-muted) focus:border-(--color-primary) focus:ring-2 focus:ring-(--color-primary)/20 focus:outline-none"
/>
</div>
@@ -147,7 +147,7 @@ export default function ReviewModal({ isOpen, onClose }: ReviewModalProps) {
<button
type="button"
onClick={handleClose}
className="flex-1 inline-flex items-center justify-center gap-2 rounded-xl border border-(--color-border) px-4 py-2.5 text-sm font-medium text-foreground transition-colors hover:bg-(--color-border-light)"
className="text-foreground inline-flex flex-1 items-center justify-center gap-2 rounded-xl border border-(--color-border) px-4 py-2.5 text-sm font-medium transition-colors hover:bg-(--color-border-light)"
>
<i className="fa-solid fa-arrow-left"></i>
Quay lại
@@ -156,7 +156,7 @@ export default function ReviewModal({ isOpen, onClose }: ReviewModalProps) {
type="button"
onClick={handleSubmit}
disabled={rating === 0}
className="flex-1 inline-flex items-center justify-center gap-2 rounded-xl bg-(--color-primary) px-4 py-2.5 text-sm font-medium text-white transition-colors hover:bg-(--color-primary-dark) disabled:cursor-not-allowed disabled:opacity-50"
className="inline-flex flex-1 items-center justify-center gap-2 rounded-xl bg-(--color-primary) px-4 py-2.5 text-sm font-medium text-white transition-colors hover:bg-(--color-primary-dark) disabled:cursor-not-allowed disabled:opacity-50"
>
<i className="fa-solid fa-check"></i>
Xác nhận
+1 -1
View File
@@ -16,7 +16,7 @@ spec:
spec:
containers:
- name: frontend-container
image: git.demonkernel.io.vn/foodsurf/frontend:latest
image: git.demonkernel.io.vn/foodsurf/frontend:1.0.10
ports:
- containerPort: 3000
resources:
+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "temp",
"version": "1.0.9",
"version": "1.0.10",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "temp",
"version": "1.0.9",
"version": "1.0.10",
"dependencies": {
"@tailwindcss/postcss": "^4.2.2",
"@types/node": "^20.19.37",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "temp",
"version": "1.0.9",
"version": "1.0.10",
"private": true,
"scripts": {
"dev": "next dev",
+11 -20
View File
@@ -20,9 +20,6 @@ importers:
next:
specifier: 16.1.7
version: 16.1.7(@babel/core@7.29.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
php:
specifier: ^1.1.0
version: 1.1.0
react:
specifier: 19.2.3
version: 19.2.3
@@ -1077,8 +1074,8 @@ packages:
resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==}
engines: {node: '>= 0.4'}
axe-core@4.11.1:
resolution: {integrity: sha512-BASOg+YwO2C+346x3LZOeoovTIoTrRqEsqMa6fmfAV0P+U9mFr9NsyOEpiYvFjbc64NMrSswhV50WdXzdb/Z5A==}
axe-core@4.11.2:
resolution: {integrity: sha512-byD6KPdvo72y/wj2T/4zGEvvlis+PsZsn/yPS3pEO+sFpcrqRpX/TJCxvVaEsNeMrfQbCr7w163YqoD9IYwHXw==}
engines: {node: '>=4'}
axobject-query@4.1.0:
@@ -1135,8 +1132,8 @@ packages:
resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==}
engines: {node: '>=8'}
browserslist@4.28.1:
resolution: {integrity: sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==}
browserslist@4.28.2:
resolution: {integrity: sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==}
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
hasBin: true
@@ -2968,10 +2965,6 @@ packages:
resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==}
engines: {node: '>=8'}
php@1.1.0:
resolution: {integrity: sha512-IUIOrFBooOyTP3SD9zZZ0cX2UwL5fQj3cmuaXGcc1gsSbMdHseNcEmpwXoh+b0G+hv5LfBL0nBggEOS2YRk7sQ==}
engines: {node: '>=18.0.0'}
picocolors@1.1.1:
resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
@@ -3903,7 +3896,7 @@ snapshots:
dependencies:
'@babel/compat-data': 7.29.0
'@babel/helper-validator-option': 7.27.1
browserslist: 4.28.1
browserslist: 4.28.2
lru-cache: 5.1.1
semver: 6.3.1
@@ -4873,7 +4866,7 @@ snapshots:
dependencies:
possible-typed-array-names: 1.1.0
axe-core@4.11.1: {}
axe-core@4.11.2: {}
axobject-query@4.1.0: {}
@@ -4934,13 +4927,13 @@ snapshots:
dependencies:
fill-range: 7.1.1
browserslist@4.28.1:
browserslist@4.28.2:
dependencies:
baseline-browser-mapping: 2.10.12
caniuse-lite: 1.0.30001782
electron-to-chromium: 1.5.329
node-releases: 2.0.36
update-browserslist-db: 1.2.3(browserslist@4.28.1)
update-browserslist-db: 1.2.3(browserslist@4.28.2)
buffer-equal-constant-time@1.0.1: {}
@@ -5504,7 +5497,7 @@ snapshots:
array-includes: 3.1.9
array.prototype.flatmap: 1.3.3
ast-types-flow: 0.0.8
axe-core: 4.11.1
axe-core: 4.11.2
axobject-query: 4.1.0
damerau-levenshtein: 1.0.8
emoji-regex: 9.2.2
@@ -6854,8 +6847,6 @@ snapshots:
path-type@4.0.0: {}
php@1.1.0: {}
picocolors@1.1.1: {}
picomatch@2.3.2: {}
@@ -7688,9 +7679,9 @@ snapshots:
untildify@3.0.3: {}
update-browserslist-db@1.2.3(browserslist@4.28.1):
update-browserslist-db@1.2.3(browserslist@4.28.2):
dependencies:
browserslist: 4.28.1
browserslist: 4.28.2
escalade: 3.2.0
picocolors: 1.1.1