Merge branch 'main' into develop
Release package / release (pull_request) Successful in 20m59s

This commit is contained in:
Thanh Quy - wolf
2026-04-03 15:13:39 +07:00
8 changed files with 75 additions and 65 deletions
+26 -15
View File
@@ -239,9 +239,9 @@ registration flow. Two-step process: phone verification → account creation.
### 4. Payment Page (app/(main)/payment/page.tsx) ### 4. Payment Page (app/(main)/payment/page.tsx)
**Route:** `/payment` **Route:** `/payment` **Type:** Client component **Description:**
**Type:** Client component Payment/checkout page. Shows cart items in a table with quantity controls and a
**Description:** Payment/checkout page. Shows cart items in a table with quantity controls and a summary aside with payment actions. summary aside with payment actions.
#### Key Features #### Key Features
@@ -254,32 +254,43 @@ registration flow. Two-step process: phone verification → account creation.
- **Invoice Aside:** - **Invoice Aside:**
- Sticky on desktop (top offset = header height + 1rem) - Sticky on desktop (top offset = header height + 1rem)
- Shows total price - Shows total price
- Payment buttons: Tiền mặt, QR Code (UI-only; trigger review modal for customers) - Payment buttons: Tiền mặt, QR Code (UI-only; trigger review modal for
- **"Đánh giá" button** — only visible when `user.role === "customer"`; opens ReviewModal customers)
- **"Quay về" button** — links back to `/`; spans full width when review button is absent - **"Đá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):** - **Review Modal (ReviewModal):**
- Opened when a customer clicks "Đánh giá" OR clicks a payment button (Tiền mặt/QR) - Opened when a customer clicks "Đánh giá" OR clicks a payment button (Tiền
- Closed via "Quay lại" button, backdrop click, or after submitting and closing mặt/QR)
- Closed via "Quay lại" button, backdrop click, or after submitting and
closing
- See `components/ReviewModal.tsx` for full documentation - See `components/ReviewModal.tsx` for full documentation
#### Context Usage #### Context Usage
- **useCart()** — items, totalPrice, increaseQty, decreaseQty, removeFromCart, setQuantity - **useCart()** — items, totalPrice, increaseQty, decreaseQty, removeFromCart,
- **useAuth()** — user (to check `user.role === "customer"` for review button visibility) setQuantity
- **useAuth()** — user (to check `user.role === "customer"` for review button
visibility)
#### State Management #### State Management
```tsx ```tsx
isReviewOpen: boolean // Controls ReviewModal visibility isReviewOpen: boolean; // Controls ReviewModal visibility
isCustomer: boolean // Derived from user.role === "customer" isCustomer: boolean; // Derived from user.role === "customer"
``` ```
#### Responsive Behavior #### Responsive Behavior
- **Mobile:** Single column layout, table scrolls horizontally, button labels hidden (icon only) - **Mobile:** Single column layout, table scrolls horizontally, button labels
- **Desktop (lg+):** Button labels visible, aside becomes sticky sidebar (xl: w-85) hidden (icon only)
- **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. - **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 ## ReviewModal
**File:** components/ReviewModal.tsx **File:** components/ReviewModal.tsx **Description:** Modal for customer
**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. 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 ### Props
| Prop | Type | Required | Default | Description | | Prop | Type | Required | Default | Description |
| ------- | ---------- | -------- | ------- | ------------------------------------------ | | ------- | ---------- | -------- | ------- | ----------------------------------------- |
| isOpen | boolean | yes | - | Controls modal visibility | | isOpen | boolean | yes | - | Controls modal visibility |
| onClose | () => void | yes | - | Callback to close the modal & reset state | | onClose | () => void | yes | - | Callback to close the modal & reset state |
### Behavior ### 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 - **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). - **Textarea:** Optional free-text review (placeholder in Vietnamese).
- **Footer buttons:** - **Footer buttons:**
- "Quay lại" — closes modal without submitting; resets all state. - "Quay lại" — closes modal without submitting; resets all state.
- "Xác nhận" — submits (UI-only); transitions to thank-you 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"). - **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 ### Styling
| Element | Key classes | | 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 | | 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 | | Active star | fa-solid fa-star text-yellow-400 |
| Inactive star | fa-regular fa-star text-(--color-border) | | Inactive star | fa-regular fa-star text-(--color-border) |
| Textarea | rounded-xl, focus:ring with --color-primary/20 | | Textarea | rounded-xl, focus:ring with --color-primary/20 |
| Quay lại button | border --color-border, hover --color-border-light | | Quay lại button | border --color-border, hover --color-border-light |
| Xác nhận button | bg --color-primary, disabled:opacity-50 | | 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 | | Thank-you icon | fa-solid fa-heart text-(--color-accent), --color-accent-light bg |
### Responsive ### Responsive
- Padding: `p-6 sm:p-8` — larger on sm+ screens - Padding: `p-6 sm:p-8` — larger on sm+ screens
- Stars: `text-3xl sm:text-4xl` - 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 ### Dependencies
- React useState - 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 - Tailwind CSS + CSS custom properties from globals.css
### Usage in Payment Page ### Usage in Payment Page
@@ -255,14 +263,14 @@ hover.
const [isReviewOpen, setIsReviewOpen] = useState(false); const [isReviewOpen, setIsReviewOpen] = useState(false);
// Button in payment aside (only visible to customers) // 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 // Payment buttons (Tiền mặt / QR Code) also open the modal for customers
const handlePayment = () => { const handlePayment = () => {
if (isCustomer) setIsReviewOpen(true); 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> </div>
<h2 <h2
id="review-modal-title" id="review-modal-title"
className="text-xl font-bold text-foreground" className="text-foreground text-xl font-bold"
> >
Cảm ơn quý khách Cảm ơn quý khách
</h2> </h2>
@@ -71,7 +71,7 @@ export default function ReviewModal({ isOpen, onClose }: ReviewModalProps) {
<> <>
<h2 <h2
id="review-modal-title" 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 Đánh giá của bạn
</h2> </h2>
@@ -138,7 +138,7 @@ export default function ReviewModal({ isOpen, onClose }: ReviewModalProps) {
onChange={(e) => setReview(e.target.value)} onChange={(e) => setReview(e.target.value)}
placeholder="Chia sẻ cảm nhận của bạn về đồ uống, dịch vụ..." placeholder="Chia sẻ cảm nhận của bạn về đồ uống, dịch vụ..."
rows={4} 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> </div>
@@ -147,7 +147,7 @@ export default function ReviewModal({ isOpen, onClose }: ReviewModalProps) {
<button <button
type="button" type="button"
onClick={handleClose} 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> <i className="fa-solid fa-arrow-left"></i>
Quay lại Quay lại
@@ -156,7 +156,7 @@ export default function ReviewModal({ isOpen, onClose }: ReviewModalProps) {
type="button" type="button"
onClick={handleSubmit} onClick={handleSubmit}
disabled={rating === 0} 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> <i className="fa-solid fa-check"></i>
Xác nhận Xác nhận
+1 -1
View File
@@ -16,7 +16,7 @@ spec:
spec: spec:
containers: containers:
- name: frontend-container - name: frontend-container
image: git.demonkernel.io.vn/foodsurf/frontend:latest image: git.demonkernel.io.vn/foodsurf/frontend:1.0.10
ports: ports:
- containerPort: 3000 - containerPort: 3000
resources: resources:
+2 -2
View File
@@ -1,12 +1,12 @@
{ {
"name": "temp", "name": "temp",
"version": "1.0.9", "version": "1.0.10",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "temp", "name": "temp",
"version": "1.0.9", "version": "1.0.10",
"dependencies": { "dependencies": {
"@tailwindcss/postcss": "^4.2.2", "@tailwindcss/postcss": "^4.2.2",
"@types/node": "^20.19.37", "@types/node": "^20.19.37",
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "temp", "name": "temp",
"version": "1.0.9", "version": "1.0.10",
"private": true, "private": true,
"scripts": { "scripts": {
"dev": "next dev", "dev": "next dev",
+11 -20
View File
@@ -20,9 +20,6 @@ importers:
next: next:
specifier: 16.1.7 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) 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: react:
specifier: 19.2.3 specifier: 19.2.3
version: 19.2.3 version: 19.2.3
@@ -1077,8 +1074,8 @@ packages:
resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
axe-core@4.11.1: axe-core@4.11.2:
resolution: {integrity: sha512-BASOg+YwO2C+346x3LZOeoovTIoTrRqEsqMa6fmfAV0P+U9mFr9NsyOEpiYvFjbc64NMrSswhV50WdXzdb/Z5A==} resolution: {integrity: sha512-byD6KPdvo72y/wj2T/4zGEvvlis+PsZsn/yPS3pEO+sFpcrqRpX/TJCxvVaEsNeMrfQbCr7w163YqoD9IYwHXw==}
engines: {node: '>=4'} engines: {node: '>=4'}
axobject-query@4.1.0: axobject-query@4.1.0:
@@ -1135,8 +1132,8 @@ packages:
resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==}
engines: {node: '>=8'} engines: {node: '>=8'}
browserslist@4.28.1: browserslist@4.28.2:
resolution: {integrity: sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==} resolution: {integrity: sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==}
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
hasBin: true hasBin: true
@@ -2968,10 +2965,6 @@ packages:
resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==}
engines: {node: '>=8'} engines: {node: '>=8'}
php@1.1.0:
resolution: {integrity: sha512-IUIOrFBooOyTP3SD9zZZ0cX2UwL5fQj3cmuaXGcc1gsSbMdHseNcEmpwXoh+b0G+hv5LfBL0nBggEOS2YRk7sQ==}
engines: {node: '>=18.0.0'}
picocolors@1.1.1: picocolors@1.1.1:
resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
@@ -3903,7 +3896,7 @@ snapshots:
dependencies: dependencies:
'@babel/compat-data': 7.29.0 '@babel/compat-data': 7.29.0
'@babel/helper-validator-option': 7.27.1 '@babel/helper-validator-option': 7.27.1
browserslist: 4.28.1 browserslist: 4.28.2
lru-cache: 5.1.1 lru-cache: 5.1.1
semver: 6.3.1 semver: 6.3.1
@@ -4873,7 +4866,7 @@ snapshots:
dependencies: dependencies:
possible-typed-array-names: 1.1.0 possible-typed-array-names: 1.1.0
axe-core@4.11.1: {} axe-core@4.11.2: {}
axobject-query@4.1.0: {} axobject-query@4.1.0: {}
@@ -4934,13 +4927,13 @@ snapshots:
dependencies: dependencies:
fill-range: 7.1.1 fill-range: 7.1.1
browserslist@4.28.1: browserslist@4.28.2:
dependencies: dependencies:
baseline-browser-mapping: 2.10.12 baseline-browser-mapping: 2.10.12
caniuse-lite: 1.0.30001782 caniuse-lite: 1.0.30001782
electron-to-chromium: 1.5.329 electron-to-chromium: 1.5.329
node-releases: 2.0.36 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: {} buffer-equal-constant-time@1.0.1: {}
@@ -5504,7 +5497,7 @@ snapshots:
array-includes: 3.1.9 array-includes: 3.1.9
array.prototype.flatmap: 1.3.3 array.prototype.flatmap: 1.3.3
ast-types-flow: 0.0.8 ast-types-flow: 0.0.8
axe-core: 4.11.1 axe-core: 4.11.2
axobject-query: 4.1.0 axobject-query: 4.1.0
damerau-levenshtein: 1.0.8 damerau-levenshtein: 1.0.8
emoji-regex: 9.2.2 emoji-regex: 9.2.2
@@ -6854,8 +6847,6 @@ snapshots:
path-type@4.0.0: {} path-type@4.0.0: {}
php@1.1.0: {}
picocolors@1.1.1: {} picocolors@1.1.1: {}
picomatch@2.3.2: {} picomatch@2.3.2: {}
@@ -7688,9 +7679,9 @@ snapshots:
untildify@3.0.3: {} 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: dependencies:
browserslist: 4.28.1 browserslist: 4.28.2
escalade: 3.2.0 escalade: 3.2.0
picocolors: 1.1.1 picocolors: 1.1.1
+1 -1
View File
@@ -19,7 +19,7 @@ const config: GlobalConfig = {
"@semantic-release/exec", "@semantic-release/exec",
{ {
prepareCmd: prepareCmd:
"node scripts/release.ts ${nextRelease.version} && sed -i 's|image: git.demonkernel.io.vn/foodsurf/frontend:.*|image: git.demonkernel.io.vn/foodsurf/frontend:${nextRelease.version}|g' k8s/k8s.yaml", "node scripts/release.ts ${nextRelease.version} && sed -i 's|image: git.demonkernel.io.vn/foodsurf/frontend:.*|image: git.demonkernel.io.vn/foodsurf/frontend:${nextRelease.version}|g' k8s.yaml",
}, },
], ],
[ [