diff --git a/microservices/apps/tpos-mvp-next/src/app/about/page.tsx b/microservices/apps/tpos-mvp-next/src/app/about/page.tsx
new file mode 100644
index 00000000..0039b635
--- /dev/null
+++ b/microservices/apps/tpos-mvp-next/src/app/about/page.tsx
@@ -0,0 +1,5 @@
+import { TposPublicLanding } from "@/components/TposPublicLanding";
+
+export default function AboutPage() {
+ return ;
+}
diff --git a/microservices/apps/tpos-mvp-next/src/app/auth/[...path]/page.tsx b/microservices/apps/tpos-mvp-next/src/app/auth/[...path]/page.tsx
index 31bda459..f626a789 100644
--- a/microservices/apps/tpos-mvp-next/src/app/auth/[...path]/page.tsx
+++ b/microservices/apps/tpos-mvp-next/src/app/auth/[...path]/page.tsx
@@ -1,7 +1,9 @@
import { TposAuthBoundary } from "@/components/TposAuthBoundary";
+import { TposLoginPortal } from "@/components/TposLoginPortal";
export default async function AuthCatchAllPage({ params }: { params: Promise<{ path?: string[] }> }) {
const path = (await params).path ?? [];
+ if (path.length === 0 || (path[0] === "login" && path.length === 1)) return ;
const mode = path.includes("register") ? "register" : path.includes("forgot-password-new") || path.includes("password-reset") || path.includes("otp-verify") || path.includes("two-factor") || path.includes("email-sent") ? "recover" : "login";
const role = path[path.length - 1] ?? "admin";
return ;
diff --git a/microservices/apps/tpos-mvp-next/src/app/gioi-thieu/page.tsx b/microservices/apps/tpos-mvp-next/src/app/gioi-thieu/page.tsx
new file mode 100644
index 00000000..001589ec
--- /dev/null
+++ b/microservices/apps/tpos-mvp-next/src/app/gioi-thieu/page.tsx
@@ -0,0 +1 @@
+export { default } from "../about/page";
diff --git a/microservices/apps/tpos-mvp-next/src/app/globals.css b/microservices/apps/tpos-mvp-next/src/app/globals.css
index 9cdf7644..c1c0770b 100644
--- a/microservices/apps/tpos-mvp-next/src/app/globals.css
+++ b/microservices/apps/tpos-mvp-next/src/app/globals.css
@@ -3615,3 +3615,870 @@ textarea {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
}
+
+/* Public landing and login portal parity */
+.public-shell {
+ min-height: 100vh;
+ background:
+ radial-gradient(circle at 50% 0%, rgba(255, 92, 0, 0.14), transparent 34%),
+ #0a0a0b;
+ color: #ffffff;
+ overflow-x: hidden;
+}
+
+.tpos-navbar {
+ position: sticky;
+ top: 0;
+ z-index: 80;
+ padding: 16px 0;
+ border-bottom: 1px solid #1f1f23;
+ background: rgba(10, 10, 11, 0.85);
+ backdrop-filter: blur(12px);
+}
+
+.tpos-navbar-inner {
+ max-width: 1200px;
+ margin: 0 auto;
+ padding: 0 24px;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 24px;
+}
+
+.tpos-logo {
+ color: #ff5c00;
+ font-size: 24px;
+ font-weight: 800;
+ text-decoration: none;
+}
+
+.tpos-nav-links {
+ display: flex;
+ align-items: center;
+ gap: 32px;
+}
+
+.tpos-nav-link {
+ color: #adadb0;
+ font-size: 14px;
+ font-weight: 500;
+ text-decoration: none;
+}
+
+.tpos-nav-link:hover {
+ color: #ffffff;
+}
+
+.btn-accent {
+ min-height: 42px;
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ gap: 8px;
+ padding: 0 24px;
+ border-radius: 10px;
+ background: #ff5c00;
+ color: #ffffff;
+ font-size: 15px;
+ font-weight: 600;
+ text-decoration: none;
+}
+
+.landing-hero {
+ position: relative;
+ min-height: 70vh;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ padding: 64px 24px 48px;
+ text-align: center;
+ overflow: hidden;
+}
+
+.landing-hero--project {
+ min-height: 62vh;
+}
+
+.landing-terminal-preview {
+ position: absolute;
+ inset: auto 5% 28px auto;
+ width: min(560px, 44vw);
+ min-height: 300px;
+ border: 1px solid rgba(255, 255, 255, 0.08);
+ border-radius: 24px;
+ background: rgba(17, 17, 19, 0.5);
+ box-shadow: 0 24px 80px rgba(0, 0, 0, 0.36);
+ opacity: 0.42;
+ transform: rotate(-2deg);
+ pointer-events: none;
+}
+
+.terminal-bar {
+ height: 46px;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: 0 16px;
+ border-bottom: 1px solid #1f1f23;
+}
+
+.terminal-bar span {
+ color: #ff5c00;
+ font-weight: 800;
+}
+
+.terminal-bar b {
+ color: #22c55e;
+ font-size: 12px;
+}
+
+.terminal-body {
+ display: grid;
+ grid-template-columns: 70px 1fr 160px;
+ gap: 14px;
+ padding: 14px;
+}
+
+.terminal-sidebar,
+.terminal-order,
+.terminal-grid i {
+ border-radius: 12px;
+ background: #1a1a1d;
+}
+
+.terminal-sidebar {
+ min-height: 220px;
+}
+
+.terminal-grid {
+ display: grid;
+ grid-template-columns: repeat(2, minmax(0, 1fr));
+ gap: 12px;
+}
+
+.terminal-grid i {
+ min-height: 104px;
+ border: 1px solid #2a2a2e;
+}
+
+.terminal-order {
+ display: grid;
+ align-content: end;
+ min-height: 220px;
+ padding: 14px;
+ color: #adadb0;
+}
+
+.terminal-order b {
+ color: #ff5c00;
+}
+
+.home-hero__badge {
+ position: relative;
+ z-index: 1;
+ display: inline-flex;
+ align-items: center;
+ gap: 8px;
+ margin-bottom: 32px;
+ padding: 8px 20px;
+ border: 1px solid rgba(255, 92, 0, 0.25);
+ border-radius: 999px;
+ background: rgba(255, 92, 0, 0.15);
+ color: #ff8a4c;
+ font-size: 13px;
+ font-weight: 600;
+ letter-spacing: 0.03em;
+}
+
+.home-hero__title {
+ position: relative;
+ z-index: 1;
+ max-width: 800px;
+ margin: 0 0 24px;
+ font-size: 56px;
+ font-weight: 800;
+ line-height: 1.08;
+ letter-spacing: -0.03em;
+ color: #ffffff;
+}
+
+.home-hero__subtitle {
+ position: relative;
+ z-index: 1;
+ max-width: 640px;
+ margin: 0 auto 40px;
+ color: #adadb0;
+ font-size: 18px;
+ line-height: 1.7;
+}
+
+.home-hero__actions {
+ position: relative;
+ z-index: 1;
+ display: flex;
+ justify-content: center;
+ flex-wrap: wrap;
+ gap: 16px;
+ margin-bottom: 48px;
+}
+
+.home-hero__btn {
+ min-height: 54px;
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ gap: 8px;
+ padding: 0 32px;
+ border-radius: 14px;
+ font-size: 16px;
+ font-weight: 700;
+ text-decoration: none;
+}
+
+.home-hero__btn--primary {
+ background: linear-gradient(135deg, #ff5c00 0%, #ff8a4c 100%);
+ color: #ffffff;
+ box-shadow: 0 4px 24px rgba(255, 92, 0, 0.3);
+}
+
+.home-hero__btn--secondary {
+ border: 1px solid #2a2a2e;
+ background: transparent;
+ color: #ffffff;
+}
+
+.home-trust {
+ position: relative;
+ z-index: 1;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 12px;
+}
+
+.home-trust__label {
+ color: #8b8b90;
+ font-size: 13px;
+ text-transform: uppercase;
+}
+
+.home-trust__stats {
+ display: flex;
+ gap: 16px;
+ color: #adadb0;
+ font-size: 14px;
+ font-weight: 600;
+}
+
+.home-verticals {
+ max-width: 900px;
+ margin: 0 auto;
+ padding: 48px 24px 64px;
+}
+
+.home-verticals__grid {
+ display: grid;
+ grid-template-columns: repeat(5, minmax(0, 1fr));
+ gap: 16px;
+}
+
+.home-vertical-card {
+ min-height: 128px;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ gap: 12px;
+ padding: 24px 16px;
+ border: 1px solid #1f1f23;
+ border-radius: 14px;
+ background: #111113;
+ color: #adadb0;
+ font-size: 13px;
+ font-weight: 600;
+ text-align: center;
+ text-decoration: none;
+}
+
+.home-vertical-card svg {
+ color: #ff5c00;
+}
+
+.home-vertical-card:hover {
+ border-color: #ff5c00;
+ background: rgba(255, 92, 0, 0.15);
+ color: #ffffff;
+}
+
+.tpos-section,
+.project-intro,
+.landing-cta {
+ max-width: 1200px;
+ margin: 0 auto;
+ padding: 72px 24px;
+}
+
+.tpos-section-header {
+ max-width: 680px;
+ margin: 0 auto 36px;
+ text-align: center;
+}
+
+.tpos-section-title,
+.project-intro h2,
+.landing-cta h2 {
+ margin: 0 0 14px;
+ color: #ffffff;
+ font-size: 36px;
+ font-weight: 800;
+ line-height: 1.15;
+}
+
+.tpos-section-desc,
+.project-intro p,
+.landing-cta p {
+ color: #adadb0;
+ font-size: 16px;
+ line-height: 1.7;
+}
+
+.tpos-feature-grid {
+ display: grid;
+ grid-template-columns: repeat(4, minmax(0, 1fr));
+ gap: 18px;
+}
+
+.tpos-feature-card,
+.tpos-pricing-card {
+ border: 1px solid #1f1f23;
+ border-radius: 16px;
+ background: #111113;
+ padding: 22px;
+}
+
+.tpos-feature-icon {
+ width: 44px;
+ height: 44px;
+ display: grid;
+ place-items: center;
+ margin-bottom: 18px;
+ border-radius: 12px;
+ background: rgba(255, 92, 0, 0.15);
+ color: #ff5c00;
+}
+
+.tpos-feature-card h3,
+.tpos-pricing-card h3 {
+ margin: 0 0 10px;
+ font-size: 18px;
+}
+
+.tpos-feature-card p,
+.tpos-pricing-card p {
+ margin: 0;
+ color: #adadb0;
+ font-size: 14px;
+ line-height: 1.65;
+}
+
+.project-intro {
+ display: grid;
+ grid-template-columns: minmax(0, 1.05fr) 420px;
+ gap: 36px;
+ align-items: center;
+}
+
+.project-stack {
+ display: grid;
+ gap: 12px;
+}
+
+.project-stack div {
+ min-height: 54px;
+ display: flex;
+ align-items: center;
+ gap: 12px;
+ padding: 0 16px;
+ border: 1px solid #2a2a2e;
+ border-radius: 12px;
+ background: #1a1a1d;
+ color: #ffffff;
+ font-weight: 600;
+}
+
+.project-stack svg {
+ color: #22c55e;
+}
+
+.tpos-pricing-grid {
+ display: grid;
+ grid-template-columns: repeat(3, minmax(0, 1fr));
+ gap: 18px;
+}
+
+.tpos-pricing-card {
+ position: relative;
+ display: grid;
+ gap: 14px;
+}
+
+.tpos-pricing-card.featured {
+ border-color: #ff5c00;
+ box-shadow: 0 0 0 1px rgba(255, 92, 0, 0.35);
+}
+
+.tpos-pricing-badge {
+ width: fit-content;
+ padding: 4px 10px;
+ border-radius: 999px;
+ background: rgba(255, 92, 0, 0.16);
+ color: #ff8a4c;
+ font-size: 12px;
+ font-weight: 700;
+}
+
+.tpos-pricing-card strong {
+ color: #ffffff;
+ font-size: 30px;
+}
+
+.tpos-pricing-card a {
+ display: inline-flex;
+ align-items: center;
+ gap: 6px;
+ color: #ff5c00;
+ font-weight: 700;
+ text-decoration: none;
+}
+
+.landing-cta {
+ display: grid;
+ justify-items: center;
+ text-align: center;
+}
+
+.landing-cta svg {
+ color: #ff5c00;
+}
+
+.landing-cta div {
+ display: flex;
+ gap: 12px;
+ margin-top: 18px;
+}
+
+.login-portal {
+ min-height: 100vh;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ padding: 80px 24px 60px;
+ background: #0a0a0b;
+ color: #ffffff;
+ font-family: var(--tpos-font);
+}
+
+.login-portal__brand {
+ position: absolute;
+ top: 24px;
+ left: 24px;
+ color: #ff5c00;
+ font-size: 24px;
+ font-weight: 800;
+ text-decoration: none;
+}
+
+.login-portal__head {
+ text-align: center;
+}
+
+.login-portal__head h1 {
+ margin: 0 0 8px;
+ font-size: 28px;
+ font-weight: 800;
+}
+
+.login-portal__head p,
+.login-portal__footer {
+ color: #adadb0;
+ font-size: 15px;
+}
+
+.login-portal__grid {
+ width: 100%;
+ max-width: 560px;
+ display: grid;
+ grid-template-columns: repeat(2, minmax(0, 1fr));
+ gap: 16px;
+ margin-top: 40px;
+}
+
+.login-role-card {
+ min-height: 176px;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 10px;
+ padding: 28px 20px;
+ border: 2px solid #2a2a2e;
+ border-radius: 12px;
+ background: #1a1a1d;
+ color: #ffffff;
+ text-align: center;
+ text-decoration: none;
+}
+
+.login-role-card:hover {
+ border-color: var(--role-color);
+ transform: translateY(-2px);
+}
+
+.login-role-card span {
+ width: 48px;
+ height: 48px;
+ display: grid;
+ place-items: center;
+ border-radius: 12px;
+ background: color-mix(in srgb, var(--role-color) 16%, transparent);
+ color: var(--role-color);
+}
+
+.login-role-card h2 {
+ margin: 0;
+ font-size: 15px;
+ font-weight: 700;
+}
+
+.login-role-card p {
+ margin: 0;
+ color: #adadb0;
+ font-size: 12px;
+ line-height: 1.4;
+}
+
+.login-portal__footer {
+ margin: 32px 0 0;
+}
+
+.login-portal__footer a,
+.login-portal__super {
+ color: #ff5c00;
+ font-weight: 600;
+ text-decoration: none;
+}
+
+.login-portal__super {
+ display: inline-flex;
+ align-items: center;
+ gap: 6px;
+ margin-top: 14px;
+ font-size: 12px;
+}
+
+@media (max-width: 820px) {
+ .tpos-nav-links {
+ gap: 16px;
+ }
+
+ .tpos-nav-link:nth-child(2),
+ .tpos-nav-link:nth-child(3),
+ .tpos-nav-link:nth-child(4) {
+ display: none;
+ }
+
+ .landing-terminal-preview {
+ display: none;
+ }
+
+ .home-hero__title {
+ font-size: 40px;
+ }
+
+ .home-verticals__grid {
+ grid-template-columns: repeat(3, minmax(0, 1fr));
+ }
+
+ .tpos-feature-grid,
+ .project-intro,
+ .tpos-pricing-grid {
+ grid-template-columns: 1fr;
+ }
+}
+
+@media (max-width: 520px) {
+ .tpos-navbar-inner {
+ padding: 0 16px;
+ }
+
+ .btn-accent {
+ display: none;
+ }
+
+ .home-hero__title {
+ font-size: 34px;
+ }
+
+ .home-hero__subtitle {
+ font-size: 15px;
+ }
+
+ .home-hero__actions,
+ .landing-cta div {
+ width: 100%;
+ flex-direction: column;
+ }
+
+ .home-hero__btn {
+ width: 100%;
+ }
+
+ .home-verticals__grid,
+ .login-portal__grid {
+ grid-template-columns: repeat(2, minmax(0, 1fr));
+ }
+}
+
+/* Auth role pages parity */
+.auth-top-nav {
+ position: sticky;
+ top: 0;
+ z-index: 90;
+ height: 72px;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: 0 24px;
+ border-bottom: 1px solid #1f1f23;
+ background: rgba(10, 10, 11, 0.88);
+ backdrop-filter: blur(12px);
+}
+
+.auth-top-nav > div {
+ display: flex;
+ align-items: center;
+ gap: 24px;
+}
+
+.auth-top-nav a:not(.btn-accent) {
+ color: #adadb0;
+ font-size: 14px;
+ font-weight: 500;
+ text-decoration: none;
+}
+
+.auth-top-nav .tpos-logo {
+ color: #ff5c00 !important;
+ font-size: 24px;
+ font-weight: 800;
+}
+
+.auth-top-nav a:hover {
+ color: #ffffff;
+}
+
+.auth-screen {
+ min-height: calc(100vh - 72px);
+}
+
+.auth-screen--split {
+ grid-template-columns: minmax(420px, 0.95fr) minmax(360px, 440px);
+ justify-content: stretch;
+ align-content: stretch;
+ gap: 0;
+ padding: 0;
+}
+
+.auth-brand-panel {
+ min-height: calc(100vh - 72px);
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ padding: 64px;
+ background:
+ radial-gradient(circle at 20% 10%, rgba(255, 255, 255, 0.16), transparent 24%),
+ linear-gradient(180deg, #ff5c00 0%, #ff8a4c 52%, #ffb347 100%);
+ color: #ffffff;
+}
+
+.auth-screen--customer .auth-brand-panel {
+ background:
+ radial-gradient(circle at 80% 20%, rgba(255, 92, 0, 0.16), transparent 30%),
+ linear-gradient(135deg, #0a0a0b 0%, #1a1a1d 50%, #0a0a0b 100%);
+}
+
+.auth-brand-mark {
+ width: 72px;
+ height: 72px;
+ display: grid;
+ place-items: center;
+ margin-bottom: 24px;
+ border-radius: 18px;
+ background: rgba(255, 255, 255, 0.18);
+}
+
+.auth-brand-panel h1 {
+ max-width: 460px;
+ margin: 0 0 16px;
+ font-size: 42px;
+ font-weight: 800;
+ line-height: 1.08;
+}
+
+.auth-brand-panel p {
+ max-width: 480px;
+ color: rgba(255, 255, 255, 0.86);
+ font-size: 16px;
+ line-height: 1.7;
+}
+
+.auth-brand-stats {
+ display: flex;
+ gap: 12px;
+ margin-top: 28px;
+}
+
+.auth-brand-stats span {
+ padding: 8px 12px;
+ border-radius: 999px;
+ background: rgba(0, 0, 0, 0.18);
+ font-size: 12px;
+ font-weight: 700;
+}
+
+.auth-screen--split .auth-card {
+ align-self: center;
+ justify-self: center;
+}
+
+.auth-card__head {
+ flex-direction: column;
+ align-items: center;
+ gap: 10px;
+ text-align: center;
+}
+
+.auth-card__head p {
+ margin: 0;
+ color: #adadb0;
+ font-size: 14px;
+ line-height: 1.5;
+}
+
+.auth-role-badge {
+ display: inline-flex;
+ align-items: center;
+ gap: 6px;
+ padding: 4px 14px;
+ border-radius: 999px;
+ font-size: 12px;
+ font-weight: 700;
+ letter-spacing: 0.03em;
+}
+
+.auth-role-badge--blue,
+.auth-card--blue .auth-submit {
+ --auth-tone: #3b82f6;
+}
+
+.auth-role-badge--green,
+.auth-card--green .auth-submit {
+ --auth-tone: #22c55e;
+}
+
+.auth-role-badge--pink,
+.auth-card--pink .auth-submit {
+ --auth-tone: #ec4899;
+}
+
+.auth-role-badge--orange,
+.auth-card--orange .auth-submit {
+ --auth-tone: #ff5c00;
+}
+
+.auth-role-badge {
+ border: 1px solid color-mix(in srgb, var(--auth-tone) 28%, transparent);
+ background: color-mix(in srgb, var(--auth-tone) 15%, transparent);
+ color: var(--auth-tone);
+}
+
+.auth-card--blue .auth-submit,
+.auth-card--green .auth-submit,
+.auth-card--pink .auth-submit,
+.auth-card--orange .auth-submit {
+ background: var(--auth-tone);
+}
+
+.auth-security-hint {
+ width: 100%;
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ margin-bottom: 16px;
+ padding: 10px 16px;
+ border-radius: 10px;
+ color: var(--auth-tone, #ff5c00);
+ background: color-mix(in srgb, var(--auth-tone, #ff5c00) 9%, transparent);
+ border: 1px solid color-mix(in srgb, var(--auth-tone, #ff5c00) 20%, transparent);
+ font-size: 13px;
+}
+
+.auth-alt-links {
+ display: flex;
+ justify-content: center;
+ flex-wrap: wrap;
+ gap: 14px;
+ margin-top: 16px;
+}
+
+.auth-alt-links a,
+.auth-inline-link {
+ color: #ff5c00;
+ font-size: 13px;
+ font-weight: 600;
+ text-decoration: none;
+}
+
+.auth-social-row {
+ display: grid;
+ gap: 10px;
+ margin: 12px 0;
+ color: #8b8b90;
+ font-size: 12px;
+ text-align: center;
+}
+
+.auth-social-row div {
+ display: grid;
+ grid-template-columns: repeat(2, minmax(0, 1fr));
+ gap: 8px;
+}
+
+.auth-social-row button {
+ min-height: 38px;
+ border: 1px solid #2a2a2e;
+ border-radius: 10px;
+ background: #1a1a1d;
+ color: #ffffff;
+ font-weight: 600;
+}
+
+@media (max-width: 820px) {
+ .auth-top-nav > div a:not(.btn-accent) {
+ display: none;
+ }
+
+ .auth-screen--split {
+ grid-template-columns: 1fr;
+ }
+
+ .auth-brand-panel {
+ display: none;
+ }
+
+ .auth-screen--split .auth-card {
+ margin: 24px;
+ }
+}
diff --git a/microservices/apps/tpos-mvp-next/src/app/login/page.tsx b/microservices/apps/tpos-mvp-next/src/app/login/page.tsx
index a525ed80..d018fffe 100644
--- a/microservices/apps/tpos-mvp-next/src/app/login/page.tsx
+++ b/microservices/apps/tpos-mvp-next/src/app/login/page.tsx
@@ -1,5 +1,5 @@
-import { TposAuthBoundary } from "@/components/TposAuthBoundary";
+import { TposLoginPortal } from "@/components/TposLoginPortal";
export default function LoginAliasPage() {
- return ;
+ return ;
}
diff --git a/microservices/apps/tpos-mvp-next/src/app/page.tsx b/microservices/apps/tpos-mvp-next/src/app/page.tsx
index b2d221dc..dc25056a 100644
--- a/microservices/apps/tpos-mvp-next/src/app/page.tsx
+++ b/microservices/apps/tpos-mvp-next/src/app/page.tsx
@@ -1,170 +1,5 @@
-import Link from "next/link";
-import { Activity, AlertTriangle, ShoppingBag, Store } from "lucide-react";
-import { createShopAction } from "@/app/actions";
-import { AppFrame } from "@/components/AppFrame";
-import { EmptyState, Metric, PageHeader, StatusPill, money } from "@/components/Primitives";
-import { getDashboardStats } from "@/server/db/queries";
-import { getShopService } from "@/server/services/shop";
-import { listActivity } from "@/server/db/queries";
-import { serviceMap, verticalOptions } from "@/server/domain/catalog";
+import { TposPublicLanding } from "@/components/TposPublicLanding";
-export const dynamic = "force-dynamic";
-
-type PageProps = {
- searchParams?: Promise<{ shopId?: string }>;
-};
-
-export default async function DashboardPage({ searchParams }: PageProps) {
- const params = (await searchParams) ?? {};
- const shop = await getShopService(params.shopId);
- const stats = await getDashboardStats(shop?.id ?? null);
- const activity = await listActivity();
-
- return (
-
-
- {shop ? (
-
-
- Mở POS
-
- ) : null}
-
-
- {!shop ? (
-
-
- Dashboard sẽ xuất hiện sau khi tạo cửa hàng đầu tiên.
-
- ) : (
- <>
-
-
-
-
- 0 ? "warn" : "neutral"} />
-
-
-
-
-
-
Đơn gần đây
- Xem tất cả
-
- {stats.recentOrders.length ? (
-
-
-
- | Mã đơn |
- Món |
- Trạng thái |
- Tổng |
-
-
-
- {stats.recentOrders.map((order) => (
-
- | {order.transactionId ?? order.id.slice(0, 8)} |
- {order.itemCount} |
-
-
- |
- {money(order.totalAmount)} |
-
- ))}
-
-
- ) : (
-
- )}
-
-
-
-
-
Service map MVP
-
-
- {serviceMap.map((service) => {
- const Icon = service.icon;
- return (
-
-
-
- {service.name}
-
- {service.label}
-
- );
- })}
-
-
-
-
- {stats.lowStock.length ? (
-
-
-
- | Mặt hàng |
- Tồn |
- Mức đặt lại |
-
-
-
- {stats.lowStock.map((item) => (
-
- | {item.name ?? item.productName} |
- {item.quantity} |
- {item.reorderLevel} |
-
- ))}
-
-
- ) : (
-
- )}
-
-
-
-
-
- {activity.map((item) => (
-
- {item.action}
- {new Date(item.createdAt).toLocaleString("vi-VN")}
-
- ))}
- {!activity.length ?
: null}
-
-
-
- >
- )}
-
- );
+export default function LandingPage() {
+ return ;
}
diff --git a/microservices/apps/tpos-mvp-next/src/app/project/page.tsx b/microservices/apps/tpos-mvp-next/src/app/project/page.tsx
new file mode 100644
index 00000000..001589ec
--- /dev/null
+++ b/microservices/apps/tpos-mvp-next/src/app/project/page.tsx
@@ -0,0 +1 @@
+export { default } from "../about/page";
diff --git a/microservices/apps/tpos-mvp-next/src/components/Shell.tsx b/microservices/apps/tpos-mvp-next/src/components/Shell.tsx
index b76ad041..7931b632 100644
--- a/microservices/apps/tpos-mvp-next/src/components/Shell.tsx
+++ b/microservices/apps/tpos-mvp-next/src/components/Shell.tsx
@@ -17,7 +17,7 @@ import type { ReactNode } from "react";
import type { Shop } from "@/server/domain/types";
const nav = [
- { href: "/", label: "Tổng quan", icon: Gauge },
+ { href: "/admin", label: "Quản trị", icon: Gauge },
{ href: "/pos", label: "Bán hàng", icon: TerminalSquare },
{ href: "/catalog", label: "Sản phẩm", icon: PackagePlus },
{ href: "/inventory", label: "Kho", icon: Boxes },
@@ -39,6 +39,7 @@ export function Shell({
const router = useRouter();
const params = useSearchParams();
const shopQuery = currentShop ? `?shopId=${currentShop.id}` : "";
+ const adminHref = currentShop ? `/admin/shop/${currentShop.id}/overview` : "/admin";
function switchShop(shopId: string) {
const next = new URLSearchParams(params.toString());
@@ -49,7 +50,7 @@ export function Shell({
return (