From f989a2f7d78673c06e8468bd78086a87166d9623 Mon Sep 17 00:00:00 2001 From: Ho Ngoc Hai Date: Sun, 4 Jan 2026 18:10:42 +0700 Subject: [PATCH] =?UTF-8?q?chore:=20X=C3=B3a=20c=C3=A1c=20t=E1=BB=87p=20c?= =?UTF-8?q?=E1=BA=A5u=20h=C3=ACnh=20v=C3=A0=20th=C3=A0nh=20ph=E1=BA=A7n=20?= =?UTF-8?q?kh=C3=B4ng=20c=C3=B2n=20s=E1=BB=AD=20d=E1=BB=A5ng=20trong=20?= =?UTF-8?q?=E1=BB=A9ng=20d=E1=BB=A5ng=20client-example,=20bao=20g=E1=BB=93?= =?UTF-8?q?m=20c=C3=A1c=20t=E1=BB=87p=20.eslintrc,=20.gitignore,=20.npmrc,?= =?UTF-8?q?=20v=C3=A0=20nhi=E1=BB=81u=20th=C3=A0nh=20ph=E1=BA=A7n=20UI=20k?= =?UTF-8?q?h=C3=A1c.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/client-example/.eslintrc.js | 6 - apps/client-example/.eslintrc.json | 98 - apps/client-example/.gitignore | 41 - apps/client-example/.npmrc | 2 - apps/client-example/.prettierrc | 13 - apps/client-example/README.md | 264 -- .../client-example/docs/ALL_FIXES_COMPLETE.md | 226 -- .../client-example/docs/FAVICON_SIMPLE_FIX.md | 52 - .../docs/FILE_GRID_NAVIGATION.md | 630 ---- .../docs/FILE_GRID_PAGINATION_EXAMPLE.md | 350 -- apps/client-example/docs/FIXES_SUMMARY.md | 105 - .../docs/FOLDER_TREE_ALL_FILES_COLLAPSE.md | 595 ---- .../docs/FOLDER_TREE_COLLAPSE_EXPAND.md | 350 -- .../docs/FOLDER_TREE_UPDATES_SUMMARY.md | 341 -- .../docs/GOOGLE_OAUTH_FRONTEND_INTEGRATION.md | 183 - apps/client-example/docs/HYDRATION_FIX.md | 71 - .../docs/HYDRATION_FIX_SIMPLE.md | 67 - .../docs/INTERNATIONALIZATION.md | 237 -- ...GUAGE_SWITCHER_DASHBOARD_IMPLEMENTATION.md | 158 - .../client-example/docs/LOGIN_REDIRECT_FIX.md | 215 -- ...ION_INTERNATIONALIZATION_IMPLEMENTATION.md | 276 -- .../docs/QUICK_START_PAGINATION.md | 159 - .../docs/STORAGE_METADATA_UPDATE.md | 117 - .../docs/STORAGE_PAGE_REFACTOR.md | 387 --- .../docs/TOKEN_EXPIRY_FIX_IMPLEMENTATION.md | 188 - apps/client-example/docs/TRADING_DEMO.md | 29 - apps/client-example/next.config.js | 77 - apps/client-example/package.json | 53 - apps/client-example/postcss.config.js | 6 - .../public/apple-touch-icon.svg | 16 - apps/client-example/public/browserconfig.xml | 9 - apps/client-example/public/favicon.svg | 16 - apps/client-example/public/file.svg | 1 - apps/client-example/public/globe.svg | 1 - apps/client-example/public/manifest.json | 25 - apps/client-example/public/next.svg | 1 - apps/client-example/public/vercel.svg | 1 - apps/client-example/public/window.svg | 1 - .../scripts/developers/debug-token.js | 83 - .../scripts/developers/login-and-set-token.js | 81 - .../scripts/developers/set-token-manually.js | 19 - .../scripts/developers/test-auth.js | 58 - .../scripts/developers/test-ppoint-direct.js | 80 - .../scripts/developers/test-token.js | 21 - .../[locale]/admin/blogs/AdminBlogsClient.tsx | 500 --- .../[locale]/admin/blogs/[id]/edit/page.tsx | 718 ---- .../app/[locale]/admin/blogs/[id]/page.tsx | 512 --- .../app/[locale]/admin/blogs/create/page.tsx | 695 ---- .../src/app/[locale]/admin/blogs/page.tsx | 50 - .../admin/challenges/[id]/edit/page.tsx | 1024 ------ .../[locale]/admin/challenges/[id]/page.tsx | 435 --- .../[id]/registration-requests/page.tsx | 26 - .../[locale]/admin/challenges/create/page.tsx | 130 - .../app/[locale]/admin/challenges/page.tsx | 660 ---- .../admin/dashboard/AdminDashboardClient.tsx | 615 ---- .../admin/dashboard/AdminDashboardServer.tsx | 50 - .../app/[locale]/admin/dashboard/page-old.tsx | 670 ---- .../src/app/[locale]/admin/dashboard/page.tsx | 50 - .../src/app/[locale]/admin/layout.tsx | 243 -- .../src/app/[locale]/admin/nfts/page.tsx | 929 ----- .../AdminOrganizationsClient.tsx | 558 --- .../app/[locale]/admin/organizations/page.tsx | 50 - .../ppoint/AdminPPointDashboardClient.tsx | 435 --- .../src/app/[locale]/admin/ppoint/page.tsx | 16 - .../[locale]/admin/roles/AdminRolesClient.tsx | 715 ---- .../src/app/[locale]/admin/roles/page.tsx | 50 - .../admin/storage/AdminStorageClient.tsx | 589 ---- .../src/app/[locale]/admin/storage/README.md | 383 --- .../src/app/[locale]/admin/storage/page.tsx | 50 - .../[locale]/admin/users/AdminUsersClient.tsx | 663 ---- .../app/[locale]/admin/users/[id]/page.tsx | 645 ---- .../src/app/[locale]/admin/users/page.tsx | 50 - .../[locale]/auth/forgot-password/layout.tsx | 53 - .../[locale]/auth/forgot-password/page.tsx | 180 - .../[locale]/auth/google/callback/page.tsx | 162 - .../src/app/[locale]/auth/layout.tsx | 50 - .../src/app/[locale]/auth/login/layout.tsx | 53 - .../src/app/[locale]/auth/login/page.tsx | 40 - .../src/app/[locale]/auth/register/layout.tsx | 53 - .../src/app/[locale]/auth/register/page.tsx | 5 - .../app/[locale]/auth/reset-password/page.tsx | 309 -- .../app/[locale]/auth/verify-email/page.tsx | 164 - .../app/[locale]/dashboard/blog/[id]/page.tsx | 399 --- .../src/app/[locale]/dashboard/blog/page.tsx | 313 -- .../src/app/[locale]/dashboard/layout.tsx | 33 - .../nft-challenge/NftChallengePageClient.tsx | 415 --- .../[id]/ChallengeDetailPageClient.tsx | 664 ---- .../dashboard/nft-challenge/[id]/page.tsx | 51 - .../[locale]/dashboard/nft-challenge/page.tsx | 52 - .../[locale]/dashboard/nft/[id]/edit/page.tsx | 524 --- .../app/[locale]/dashboard/nft/[id]/page.tsx | 447 --- .../[locale]/dashboard/nft/create/page.tsx | 171 - .../src/app/[locale]/dashboard/nft/page.tsx | 484 --- .../app/[locale]/dashboard/page-metadata.tsx | 32 - .../src/app/[locale]/dashboard/page.tsx | 88 - .../ppoint/PPointDashboardClient.tsx | 439 --- .../app/[locale]/dashboard/ppoint/page.tsx | 24 - .../app/[locale]/dashboard/storage/layout.tsx | 19 - .../[locale]/dashboard/storage/metadata.ts | 45 - .../app/[locale]/dashboard/storage/page.tsx | 417 --- .../[locale]/dashboard/trading-demo/page.tsx | 171 - .../client-example/src/app/[locale]/error.tsx | 51 - .../src/app/[locale]/layout.tsx | 135 - .../src/app/[locale]/not-found.tsx | 32 - apps/client-example/src/app/[locale]/page.tsx | 44 - .../src/app/[locale]/profile/README.md | 202 -- .../profile/components/BusinessInfo.tsx | 428 --- .../profile/components/ProfileHeader.tsx | 66 - .../profile/components/ProfileSections.tsx | 395 --- .../profile/components/ProfileSidebar.tsx | 156 - .../profile/constants/profile.constants.ts | 288 -- .../app/[locale]/profile/hooks/useProfile.ts | 189 - .../src/app/[locale]/profile/layout.tsx | 50 - .../src/app/[locale]/profile/page.tsx | 201 -- .../[locale]/profile/types/profile.types.ts | 97 - .../[locale]/profile/utils/profile.utils.ts | 221 -- .../src/app/[locale]/settings/page.tsx | 366 -- .../[locale]/shared/[shareToken]/README.md | 68 - .../app/[locale]/shared/[shareToken]/page.tsx | 458 --- .../src/app/[locale]/shared/layout.tsx | 16 - apps/client-example/src/app/global-error.tsx | 38 - apps/client-example/src/app/globals.css | 281 -- apps/client-example/src/app/layout.tsx | 46 - apps/client-example/src/app/page.tsx | 8 - apps/client-example/src/app/robots.ts | 18 - apps/client-example/src/app/sitemap.ts | 38 - .../src/components/admin/AddMemberModal.tsx | 325 -- .../src/components/admin/AdminLayout.tsx | 19 - .../src/components/admin/AdminNavigation.tsx | 304 -- .../admin/AdminOrganizationRequests.tsx | 459 --- .../admin/AdminRegistrationRequests.tsx | 438 --- .../src/components/admin/AssignOwnerModal.tsx | 306 -- .../admin/CreateOrganizationModal.tsx | 241 -- .../src/components/admin/CreateUserModal.tsx | 266 -- .../admin/EditOrganizationModal.tsx | 248 -- .../admin/OrganizationRequestReviewModal.tsx | 435 --- .../components/admin/RoleAssignmentModal.tsx | 242 -- .../admin/RolePermissionManager.tsx | 318 -- .../admin/ViewOrganizationModal.tsx | 377 -- .../admin/blog/AdminBlogAnalytics.tsx | 467 --- .../admin/blog/AdminBlogMediaManager.tsx | 979 ------ .../components/admin/blog/BlogBulkActions.tsx | 104 - .../src/components/admin/blog/BlogFilters.tsx | 150 - .../components/admin/blog/BlogStatsCards.tsx | 59 - .../src/components/admin/blog/BlogTable.tsx | 257 -- .../admin/blog/CategoryManagement.tsx | 475 --- .../components/admin/blog/CreateBlogModal.tsx | 147 - .../src/components/admin/blog/index.ts | 7 - .../participants/ParticipantEditModal.tsx | 341 -- .../admin/participants/ParticipantList.tsx | 548 --- .../src/components/admin/rounds/RoundCard.tsx | 289 -- .../src/components/admin/rounds/RoundForm.tsx | 689 ---- .../src/components/admin/rounds/RoundList.tsx | 261 -- .../admin/submissions/SubmissionList.tsx | 449 --- .../submissions/SubmissionPreviewModal.tsx | 496 --- .../submissions/SubmissionReviewModal.tsx | 247 -- .../components/auth/GoogleSignInButton.tsx | 125 - .../src/components/auth/LoginForm.tsx | 312 -- .../src/components/auth/ProtectedRoute.tsx | 70 - .../src/components/auth/RegisterForm.tsx | 421 --- .../src/components/auth/index.ts | 6 - .../src/components/blog/BlogDetail.tsx | 586 ---- .../src/components/blog/BlogList.tsx | 612 ---- .../src/components/blog/BlogMediaManager.tsx | 261 -- .../components/blog/CKEditorIntegration.tsx | 520 --- .../src/components/blog/CreateBlogForm.tsx | 693 ---- .../components/blog/FeaturedImagePicker.tsx | 209 -- .../src/components/blog/index.ts | 11 - .../src/components/challenge/ImageUpload.tsx | 323 -- .../challenge/JoinChallengeForm.tsx | 243 -- .../src/components/challenge/PaymentModal.tsx | 244 -- .../components/challenge/PaymentStatus.tsx | 225 -- .../components/challenge/SubmissionForm.tsx | 729 ---- .../challenge/SubmissionGallery.tsx | 1222 ------- .../src/components/nft/BlockchainSelector.tsx | 121 - .../components/nft/CopyrightMintingFlow.tsx | 600 ---- .../src/components/nft/CreateNFTForm.tsx | 334 -- .../src/components/nft/NFTCard.tsx | 258 -- .../src/components/nft/NFTChallengeList.tsx | 285 -- .../src/components/nft/NFTFeedSection.tsx | 250 -- .../src/components/nft/NFTGallery.tsx | 396 --- .../components/nft/NFTManagementSection.tsx | 297 -- .../src/components/nft/NFTMintingFlow.tsx | 458 --- .../src/components/nft/NFTMintingModal.tsx | 169 - .../src/components/nft/NFTNewsSection.tsx | 182 - .../src/components/nft/NFTSocialDashboard.tsx | 66 - .../src/components/nft/NFTUserProfileCard.tsx | 97 - .../components/nft/StandardMintingFlow.tsx | 248 -- .../organization/OrganizationEditForm.tsx | 514 --- .../organization/OrganizationManagement.tsx | 293 -- .../OrganizationRequestDetail.tsx | 323 -- .../organization/OrganizationRequestForm.tsx | 654 ---- .../src/components/organization/index.ts | 7 - .../organization/utils/organization.utils.ts | 209 -- .../src/components/ppoint/DailyPointsCard.tsx | 185 - .../src/components/ppoint/EmptyState.tsx | 77 - .../src/components/ppoint/PointsAnalytics.tsx | 247 -- .../components/ppoint/PointsLeaderboard.tsx | 196 -- .../src/components/ppoint/PointsOverview.tsx | 133 - .../src/components/ppoint/PointsRedeem.tsx | 262 -- .../src/components/ppoint/TaskList.tsx | 283 -- .../components/ppoint/TransactionHistory.tsx | 212 -- .../ppoint/admin/AdminAnalytics.tsx | 171 - .../ppoint/admin/AdminPointsManagement.tsx | 484 --- .../ppoint/admin/AdminPointsOverview.tsx | 401 --- .../ppoint/admin/AdminSystemConfig.tsx | 265 -- .../src/components/ppoint/index.ts | 15 - .../profile/OrganizationDetails.tsx | 429 --- .../src/components/profile/PasswordChange.tsx | 263 -- .../components/profile/SecuritySettings.tsx | 490 --- .../src/components/seo/JsonLd.tsx | 34 - .../src/components/storage/AdvancedSearch.tsx | 514 --- .../src/components/storage/BulkActions.tsx | 478 --- .../components/storage/DuplicateManager.tsx | 508 --- .../src/components/storage/FileGrid.tsx | 432 --- .../components/storage/FileGridNavigation.tsx | 177 - .../src/components/storage/FileIcon.tsx | 88 - .../src/components/storage/FileUploadZone.tsx | 236 -- .../src/components/storage/FileVersioning.tsx | 517 --- .../src/components/storage/FolderTree.tsx | 473 --- .../src/components/storage/MediaViewer.tsx | 523 --- .../src/components/storage/ShareModal.tsx | 476 --- .../components/storage/SharedMediaViewer.tsx | 499 --- .../components/storage/StorageAnalytics.tsx | 646 ---- .../components/storage/StoragePageContent.tsx | 356 -- .../components/storage/StoragePageHeader.tsx | 139 - .../components/storage/StoragePageModals.tsx | 300 -- .../components/storage/StoragePageSidebar.tsx | 157 - .../src/components/storage/StorageQuota.tsx | 224 -- .../src/components/storage/index.ts | 26 - .../components/storage/storage-page.utils.ts | 97 - .../src/components/trading/OrderSimulator.tsx | 95 - .../trading/TradingHistoryChart.tsx | 268 -- .../components/trading/TradingStatsCards.tsx | 84 - .../src/components/ui/Badge.tsx | 26 - .../src/components/ui/Button.tsx | 76 - .../client-example/src/components/ui/Card.tsx | 88 - .../src/components/ui/Input.tsx | 42 - .../src/components/ui/Label.tsx | 18 - .../src/components/ui/LanguageSwitcher.tsx | 62 - .../src/components/ui/MinimalBadge.tsx | 39 - .../src/components/ui/MinimalButton.tsx | 62 - .../src/components/ui/MinimalCard.tsx | 43 - .../src/components/ui/MinimalInput.tsx | 62 - .../src/components/ui/MinimalLoading.tsx | 92 - .../src/components/ui/Navigation.tsx | 375 -- .../src/components/ui/Notification.tsx | 130 - .../src/components/ui/Pagination.tsx | 123 - .../src/components/ui/Progress.tsx | 53 - .../src/components/ui/Textarea.tsx | 43 - .../src/components/ui/ThemeSwitcher.tsx | 71 - .../src/components/ui/Toast.tsx | 260 -- .../client-example/src/components/ui/index.ts | 24 - apps/client-example/src/config/blockchain.ts | 60 - .../src/config/payment.config.ts | 34 - .../src/contexts/AuthContext.tsx | 530 --- .../src/contexts/NotificationContext.tsx | 93 - .../src/contexts/ThemeContext.tsx | 72 - .../src/hooks/useMarketSocket.ts | 93 - apps/client-example/src/hooks/useStorage.ts | 472 --- .../src/hooks/useTradingDemo.ts | 282 -- apps/client-example/src/i18n/config.ts | 28 - apps/client-example/src/i18n/request.ts | 20 - .../src/lib/api-base-url.utils.ts | 42 - apps/client-example/src/lib/auth.service.ts | 763 ---- .../src/lib/blog-media.service.ts | 417 --- apps/client-example/src/lib/blog.service.ts | 1101 ------ .../src/lib/category.service.ts | 347 -- .../src/lib/challenge.service.ts | 864 ----- apps/client-example/src/lib/image-proxy.ts | 64 - .../src/lib/market-data.service.ts | 109 - apps/client-example/src/lib/nft.service.ts | 357 -- .../src/lib/organization.service.ts | 455 --- apps/client-example/src/lib/ppoint.service.ts | 450 --- .../src/lib/schema-generators.ts | 89 - .../src/lib/storage-url.utils.ts | 117 - .../client-example/src/lib/storage.service.ts | 855 ----- .../src/lib/submission.service.ts | 422 --- apps/client-example/src/lib/user.service.ts | 1010 ------ apps/client-example/src/lib/utils.ts | 43 - apps/client-example/src/messages/en.json | 3056 ----------------- apps/client-example/src/messages/vi.json | 2934 ---------------- apps/client-example/src/middleware.ts | 131 - apps/client-example/src/types/auth.ts | 164 - apps/client-example/src/types/nft.ts | 227 -- apps/client-example/src/types/storage.ts | 324 -- apps/client-example/tailwind.config.js | 121 - apps/client-example/test/cookies.txt | 5 - apps/client-example/test/debug-env.js | 5 - .../test/test-admin-ppoint-dashboard.js | 244 -- .../client-example/test/test-pagination-ui.js | 164 - .../test/test-ppoint-data-fix.js | 158 - .../test/test-submission-token.js | 40 - apps/client-example/tsconfig.json | 27 - 294 files changed, 79423 deletions(-) delete mode 100644 apps/client-example/.eslintrc.js delete mode 100644 apps/client-example/.eslintrc.json delete mode 100644 apps/client-example/.gitignore delete mode 100644 apps/client-example/.npmrc delete mode 100644 apps/client-example/.prettierrc delete mode 100644 apps/client-example/README.md delete mode 100644 apps/client-example/docs/ALL_FIXES_COMPLETE.md delete mode 100644 apps/client-example/docs/FAVICON_SIMPLE_FIX.md delete mode 100644 apps/client-example/docs/FILE_GRID_NAVIGATION.md delete mode 100644 apps/client-example/docs/FILE_GRID_PAGINATION_EXAMPLE.md delete mode 100644 apps/client-example/docs/FIXES_SUMMARY.md delete mode 100644 apps/client-example/docs/FOLDER_TREE_ALL_FILES_COLLAPSE.md delete mode 100644 apps/client-example/docs/FOLDER_TREE_COLLAPSE_EXPAND.md delete mode 100644 apps/client-example/docs/FOLDER_TREE_UPDATES_SUMMARY.md delete mode 100644 apps/client-example/docs/GOOGLE_OAUTH_FRONTEND_INTEGRATION.md delete mode 100644 apps/client-example/docs/HYDRATION_FIX.md delete mode 100644 apps/client-example/docs/HYDRATION_FIX_SIMPLE.md delete mode 100644 apps/client-example/docs/INTERNATIONALIZATION.md delete mode 100644 apps/client-example/docs/LANGUAGE_SWITCHER_DASHBOARD_IMPLEMENTATION.md delete mode 100644 apps/client-example/docs/LOGIN_REDIRECT_FIX.md delete mode 100644 apps/client-example/docs/NAVIGATION_INTERNATIONALIZATION_IMPLEMENTATION.md delete mode 100644 apps/client-example/docs/QUICK_START_PAGINATION.md delete mode 100644 apps/client-example/docs/STORAGE_METADATA_UPDATE.md delete mode 100644 apps/client-example/docs/STORAGE_PAGE_REFACTOR.md delete mode 100644 apps/client-example/docs/TOKEN_EXPIRY_FIX_IMPLEMENTATION.md delete mode 100644 apps/client-example/docs/TRADING_DEMO.md delete mode 100644 apps/client-example/next.config.js delete mode 100644 apps/client-example/package.json delete mode 100644 apps/client-example/postcss.config.js delete mode 100644 apps/client-example/public/apple-touch-icon.svg delete mode 100644 apps/client-example/public/browserconfig.xml delete mode 100644 apps/client-example/public/favicon.svg delete mode 100644 apps/client-example/public/file.svg delete mode 100644 apps/client-example/public/globe.svg delete mode 100644 apps/client-example/public/manifest.json delete mode 100644 apps/client-example/public/next.svg delete mode 100644 apps/client-example/public/vercel.svg delete mode 100644 apps/client-example/public/window.svg delete mode 100644 apps/client-example/scripts/developers/debug-token.js delete mode 100644 apps/client-example/scripts/developers/login-and-set-token.js delete mode 100644 apps/client-example/scripts/developers/set-token-manually.js delete mode 100644 apps/client-example/scripts/developers/test-auth.js delete mode 100644 apps/client-example/scripts/developers/test-ppoint-direct.js delete mode 100644 apps/client-example/scripts/developers/test-token.js delete mode 100644 apps/client-example/src/app/[locale]/admin/blogs/AdminBlogsClient.tsx delete mode 100644 apps/client-example/src/app/[locale]/admin/blogs/[id]/edit/page.tsx delete mode 100644 apps/client-example/src/app/[locale]/admin/blogs/[id]/page.tsx delete mode 100644 apps/client-example/src/app/[locale]/admin/blogs/create/page.tsx delete mode 100644 apps/client-example/src/app/[locale]/admin/blogs/page.tsx delete mode 100644 apps/client-example/src/app/[locale]/admin/challenges/[id]/edit/page.tsx delete mode 100644 apps/client-example/src/app/[locale]/admin/challenges/[id]/page.tsx delete mode 100644 apps/client-example/src/app/[locale]/admin/challenges/[id]/registration-requests/page.tsx delete mode 100644 apps/client-example/src/app/[locale]/admin/challenges/create/page.tsx delete mode 100644 apps/client-example/src/app/[locale]/admin/challenges/page.tsx delete mode 100644 apps/client-example/src/app/[locale]/admin/dashboard/AdminDashboardClient.tsx delete mode 100644 apps/client-example/src/app/[locale]/admin/dashboard/AdminDashboardServer.tsx delete mode 100644 apps/client-example/src/app/[locale]/admin/dashboard/page-old.tsx delete mode 100644 apps/client-example/src/app/[locale]/admin/dashboard/page.tsx delete mode 100644 apps/client-example/src/app/[locale]/admin/layout.tsx delete mode 100644 apps/client-example/src/app/[locale]/admin/nfts/page.tsx delete mode 100644 apps/client-example/src/app/[locale]/admin/organizations/AdminOrganizationsClient.tsx delete mode 100644 apps/client-example/src/app/[locale]/admin/organizations/page.tsx delete mode 100644 apps/client-example/src/app/[locale]/admin/ppoint/AdminPPointDashboardClient.tsx delete mode 100644 apps/client-example/src/app/[locale]/admin/ppoint/page.tsx delete mode 100644 apps/client-example/src/app/[locale]/admin/roles/AdminRolesClient.tsx delete mode 100644 apps/client-example/src/app/[locale]/admin/roles/page.tsx delete mode 100644 apps/client-example/src/app/[locale]/admin/storage/AdminStorageClient.tsx delete mode 100644 apps/client-example/src/app/[locale]/admin/storage/README.md delete mode 100644 apps/client-example/src/app/[locale]/admin/storage/page.tsx delete mode 100644 apps/client-example/src/app/[locale]/admin/users/AdminUsersClient.tsx delete mode 100644 apps/client-example/src/app/[locale]/admin/users/[id]/page.tsx delete mode 100644 apps/client-example/src/app/[locale]/admin/users/page.tsx delete mode 100644 apps/client-example/src/app/[locale]/auth/forgot-password/layout.tsx delete mode 100644 apps/client-example/src/app/[locale]/auth/forgot-password/page.tsx delete mode 100644 apps/client-example/src/app/[locale]/auth/google/callback/page.tsx delete mode 100644 apps/client-example/src/app/[locale]/auth/layout.tsx delete mode 100644 apps/client-example/src/app/[locale]/auth/login/layout.tsx delete mode 100644 apps/client-example/src/app/[locale]/auth/login/page.tsx delete mode 100644 apps/client-example/src/app/[locale]/auth/register/layout.tsx delete mode 100644 apps/client-example/src/app/[locale]/auth/register/page.tsx delete mode 100644 apps/client-example/src/app/[locale]/auth/reset-password/page.tsx delete mode 100644 apps/client-example/src/app/[locale]/auth/verify-email/page.tsx delete mode 100644 apps/client-example/src/app/[locale]/dashboard/blog/[id]/page.tsx delete mode 100644 apps/client-example/src/app/[locale]/dashboard/blog/page.tsx delete mode 100644 apps/client-example/src/app/[locale]/dashboard/layout.tsx delete mode 100644 apps/client-example/src/app/[locale]/dashboard/nft-challenge/NftChallengePageClient.tsx delete mode 100644 apps/client-example/src/app/[locale]/dashboard/nft-challenge/[id]/ChallengeDetailPageClient.tsx delete mode 100644 apps/client-example/src/app/[locale]/dashboard/nft-challenge/[id]/page.tsx delete mode 100644 apps/client-example/src/app/[locale]/dashboard/nft-challenge/page.tsx delete mode 100644 apps/client-example/src/app/[locale]/dashboard/nft/[id]/edit/page.tsx delete mode 100644 apps/client-example/src/app/[locale]/dashboard/nft/[id]/page.tsx delete mode 100644 apps/client-example/src/app/[locale]/dashboard/nft/create/page.tsx delete mode 100644 apps/client-example/src/app/[locale]/dashboard/nft/page.tsx delete mode 100644 apps/client-example/src/app/[locale]/dashboard/page-metadata.tsx delete mode 100644 apps/client-example/src/app/[locale]/dashboard/page.tsx delete mode 100644 apps/client-example/src/app/[locale]/dashboard/ppoint/PPointDashboardClient.tsx delete mode 100644 apps/client-example/src/app/[locale]/dashboard/ppoint/page.tsx delete mode 100644 apps/client-example/src/app/[locale]/dashboard/storage/layout.tsx delete mode 100644 apps/client-example/src/app/[locale]/dashboard/storage/metadata.ts delete mode 100644 apps/client-example/src/app/[locale]/dashboard/storage/page.tsx delete mode 100644 apps/client-example/src/app/[locale]/dashboard/trading-demo/page.tsx delete mode 100644 apps/client-example/src/app/[locale]/error.tsx delete mode 100644 apps/client-example/src/app/[locale]/layout.tsx delete mode 100644 apps/client-example/src/app/[locale]/not-found.tsx delete mode 100644 apps/client-example/src/app/[locale]/page.tsx delete mode 100644 apps/client-example/src/app/[locale]/profile/README.md delete mode 100644 apps/client-example/src/app/[locale]/profile/components/BusinessInfo.tsx delete mode 100644 apps/client-example/src/app/[locale]/profile/components/ProfileHeader.tsx delete mode 100644 apps/client-example/src/app/[locale]/profile/components/ProfileSections.tsx delete mode 100644 apps/client-example/src/app/[locale]/profile/components/ProfileSidebar.tsx delete mode 100644 apps/client-example/src/app/[locale]/profile/constants/profile.constants.ts delete mode 100644 apps/client-example/src/app/[locale]/profile/hooks/useProfile.ts delete mode 100644 apps/client-example/src/app/[locale]/profile/layout.tsx delete mode 100644 apps/client-example/src/app/[locale]/profile/page.tsx delete mode 100644 apps/client-example/src/app/[locale]/profile/types/profile.types.ts delete mode 100644 apps/client-example/src/app/[locale]/profile/utils/profile.utils.ts delete mode 100644 apps/client-example/src/app/[locale]/settings/page.tsx delete mode 100644 apps/client-example/src/app/[locale]/shared/[shareToken]/README.md delete mode 100644 apps/client-example/src/app/[locale]/shared/[shareToken]/page.tsx delete mode 100644 apps/client-example/src/app/[locale]/shared/layout.tsx delete mode 100644 apps/client-example/src/app/global-error.tsx delete mode 100644 apps/client-example/src/app/globals.css delete mode 100644 apps/client-example/src/app/layout.tsx delete mode 100644 apps/client-example/src/app/page.tsx delete mode 100644 apps/client-example/src/app/robots.ts delete mode 100644 apps/client-example/src/app/sitemap.ts delete mode 100644 apps/client-example/src/components/admin/AddMemberModal.tsx delete mode 100644 apps/client-example/src/components/admin/AdminLayout.tsx delete mode 100644 apps/client-example/src/components/admin/AdminNavigation.tsx delete mode 100644 apps/client-example/src/components/admin/AdminOrganizationRequests.tsx delete mode 100644 apps/client-example/src/components/admin/AdminRegistrationRequests.tsx delete mode 100644 apps/client-example/src/components/admin/AssignOwnerModal.tsx delete mode 100644 apps/client-example/src/components/admin/CreateOrganizationModal.tsx delete mode 100644 apps/client-example/src/components/admin/CreateUserModal.tsx delete mode 100644 apps/client-example/src/components/admin/EditOrganizationModal.tsx delete mode 100644 apps/client-example/src/components/admin/OrganizationRequestReviewModal.tsx delete mode 100644 apps/client-example/src/components/admin/RoleAssignmentModal.tsx delete mode 100644 apps/client-example/src/components/admin/RolePermissionManager.tsx delete mode 100644 apps/client-example/src/components/admin/ViewOrganizationModal.tsx delete mode 100644 apps/client-example/src/components/admin/blog/AdminBlogAnalytics.tsx delete mode 100644 apps/client-example/src/components/admin/blog/AdminBlogMediaManager.tsx delete mode 100644 apps/client-example/src/components/admin/blog/BlogBulkActions.tsx delete mode 100644 apps/client-example/src/components/admin/blog/BlogFilters.tsx delete mode 100644 apps/client-example/src/components/admin/blog/BlogStatsCards.tsx delete mode 100644 apps/client-example/src/components/admin/blog/BlogTable.tsx delete mode 100644 apps/client-example/src/components/admin/blog/CategoryManagement.tsx delete mode 100644 apps/client-example/src/components/admin/blog/CreateBlogModal.tsx delete mode 100644 apps/client-example/src/components/admin/blog/index.ts delete mode 100644 apps/client-example/src/components/admin/participants/ParticipantEditModal.tsx delete mode 100644 apps/client-example/src/components/admin/participants/ParticipantList.tsx delete mode 100644 apps/client-example/src/components/admin/rounds/RoundCard.tsx delete mode 100644 apps/client-example/src/components/admin/rounds/RoundForm.tsx delete mode 100644 apps/client-example/src/components/admin/rounds/RoundList.tsx delete mode 100644 apps/client-example/src/components/admin/submissions/SubmissionList.tsx delete mode 100644 apps/client-example/src/components/admin/submissions/SubmissionPreviewModal.tsx delete mode 100644 apps/client-example/src/components/admin/submissions/SubmissionReviewModal.tsx delete mode 100644 apps/client-example/src/components/auth/GoogleSignInButton.tsx delete mode 100644 apps/client-example/src/components/auth/LoginForm.tsx delete mode 100644 apps/client-example/src/components/auth/ProtectedRoute.tsx delete mode 100644 apps/client-example/src/components/auth/RegisterForm.tsx delete mode 100644 apps/client-example/src/components/auth/index.ts delete mode 100644 apps/client-example/src/components/blog/BlogDetail.tsx delete mode 100644 apps/client-example/src/components/blog/BlogList.tsx delete mode 100644 apps/client-example/src/components/blog/BlogMediaManager.tsx delete mode 100644 apps/client-example/src/components/blog/CKEditorIntegration.tsx delete mode 100644 apps/client-example/src/components/blog/CreateBlogForm.tsx delete mode 100644 apps/client-example/src/components/blog/FeaturedImagePicker.tsx delete mode 100644 apps/client-example/src/components/blog/index.ts delete mode 100644 apps/client-example/src/components/challenge/ImageUpload.tsx delete mode 100644 apps/client-example/src/components/challenge/JoinChallengeForm.tsx delete mode 100644 apps/client-example/src/components/challenge/PaymentModal.tsx delete mode 100644 apps/client-example/src/components/challenge/PaymentStatus.tsx delete mode 100644 apps/client-example/src/components/challenge/SubmissionForm.tsx delete mode 100644 apps/client-example/src/components/challenge/SubmissionGallery.tsx delete mode 100644 apps/client-example/src/components/nft/BlockchainSelector.tsx delete mode 100644 apps/client-example/src/components/nft/CopyrightMintingFlow.tsx delete mode 100644 apps/client-example/src/components/nft/CreateNFTForm.tsx delete mode 100644 apps/client-example/src/components/nft/NFTCard.tsx delete mode 100644 apps/client-example/src/components/nft/NFTChallengeList.tsx delete mode 100644 apps/client-example/src/components/nft/NFTFeedSection.tsx delete mode 100644 apps/client-example/src/components/nft/NFTGallery.tsx delete mode 100644 apps/client-example/src/components/nft/NFTManagementSection.tsx delete mode 100644 apps/client-example/src/components/nft/NFTMintingFlow.tsx delete mode 100644 apps/client-example/src/components/nft/NFTMintingModal.tsx delete mode 100644 apps/client-example/src/components/nft/NFTNewsSection.tsx delete mode 100644 apps/client-example/src/components/nft/NFTSocialDashboard.tsx delete mode 100644 apps/client-example/src/components/nft/NFTUserProfileCard.tsx delete mode 100644 apps/client-example/src/components/nft/StandardMintingFlow.tsx delete mode 100644 apps/client-example/src/components/organization/OrganizationEditForm.tsx delete mode 100644 apps/client-example/src/components/organization/OrganizationManagement.tsx delete mode 100644 apps/client-example/src/components/organization/OrganizationRequestDetail.tsx delete mode 100644 apps/client-example/src/components/organization/OrganizationRequestForm.tsx delete mode 100644 apps/client-example/src/components/organization/index.ts delete mode 100644 apps/client-example/src/components/organization/utils/organization.utils.ts delete mode 100644 apps/client-example/src/components/ppoint/DailyPointsCard.tsx delete mode 100644 apps/client-example/src/components/ppoint/EmptyState.tsx delete mode 100644 apps/client-example/src/components/ppoint/PointsAnalytics.tsx delete mode 100644 apps/client-example/src/components/ppoint/PointsLeaderboard.tsx delete mode 100644 apps/client-example/src/components/ppoint/PointsOverview.tsx delete mode 100644 apps/client-example/src/components/ppoint/PointsRedeem.tsx delete mode 100644 apps/client-example/src/components/ppoint/TaskList.tsx delete mode 100644 apps/client-example/src/components/ppoint/TransactionHistory.tsx delete mode 100644 apps/client-example/src/components/ppoint/admin/AdminAnalytics.tsx delete mode 100644 apps/client-example/src/components/ppoint/admin/AdminPointsManagement.tsx delete mode 100644 apps/client-example/src/components/ppoint/admin/AdminPointsOverview.tsx delete mode 100644 apps/client-example/src/components/ppoint/admin/AdminSystemConfig.tsx delete mode 100644 apps/client-example/src/components/ppoint/index.ts delete mode 100644 apps/client-example/src/components/profile/OrganizationDetails.tsx delete mode 100644 apps/client-example/src/components/profile/PasswordChange.tsx delete mode 100644 apps/client-example/src/components/profile/SecuritySettings.tsx delete mode 100644 apps/client-example/src/components/seo/JsonLd.tsx delete mode 100644 apps/client-example/src/components/storage/AdvancedSearch.tsx delete mode 100644 apps/client-example/src/components/storage/BulkActions.tsx delete mode 100644 apps/client-example/src/components/storage/DuplicateManager.tsx delete mode 100644 apps/client-example/src/components/storage/FileGrid.tsx delete mode 100644 apps/client-example/src/components/storage/FileGridNavigation.tsx delete mode 100644 apps/client-example/src/components/storage/FileIcon.tsx delete mode 100644 apps/client-example/src/components/storage/FileUploadZone.tsx delete mode 100644 apps/client-example/src/components/storage/FileVersioning.tsx delete mode 100644 apps/client-example/src/components/storage/FolderTree.tsx delete mode 100644 apps/client-example/src/components/storage/MediaViewer.tsx delete mode 100644 apps/client-example/src/components/storage/ShareModal.tsx delete mode 100644 apps/client-example/src/components/storage/SharedMediaViewer.tsx delete mode 100644 apps/client-example/src/components/storage/StorageAnalytics.tsx delete mode 100644 apps/client-example/src/components/storage/StoragePageContent.tsx delete mode 100644 apps/client-example/src/components/storage/StoragePageHeader.tsx delete mode 100644 apps/client-example/src/components/storage/StoragePageModals.tsx delete mode 100644 apps/client-example/src/components/storage/StoragePageSidebar.tsx delete mode 100644 apps/client-example/src/components/storage/StorageQuota.tsx delete mode 100644 apps/client-example/src/components/storage/index.ts delete mode 100644 apps/client-example/src/components/storage/storage-page.utils.ts delete mode 100644 apps/client-example/src/components/trading/OrderSimulator.tsx delete mode 100644 apps/client-example/src/components/trading/TradingHistoryChart.tsx delete mode 100644 apps/client-example/src/components/trading/TradingStatsCards.tsx delete mode 100644 apps/client-example/src/components/ui/Badge.tsx delete mode 100644 apps/client-example/src/components/ui/Button.tsx delete mode 100644 apps/client-example/src/components/ui/Card.tsx delete mode 100644 apps/client-example/src/components/ui/Input.tsx delete mode 100644 apps/client-example/src/components/ui/Label.tsx delete mode 100644 apps/client-example/src/components/ui/LanguageSwitcher.tsx delete mode 100644 apps/client-example/src/components/ui/MinimalBadge.tsx delete mode 100644 apps/client-example/src/components/ui/MinimalButton.tsx delete mode 100644 apps/client-example/src/components/ui/MinimalCard.tsx delete mode 100644 apps/client-example/src/components/ui/MinimalInput.tsx delete mode 100644 apps/client-example/src/components/ui/MinimalLoading.tsx delete mode 100644 apps/client-example/src/components/ui/Navigation.tsx delete mode 100644 apps/client-example/src/components/ui/Notification.tsx delete mode 100644 apps/client-example/src/components/ui/Pagination.tsx delete mode 100644 apps/client-example/src/components/ui/Progress.tsx delete mode 100644 apps/client-example/src/components/ui/Textarea.tsx delete mode 100644 apps/client-example/src/components/ui/ThemeSwitcher.tsx delete mode 100644 apps/client-example/src/components/ui/Toast.tsx delete mode 100644 apps/client-example/src/components/ui/index.ts delete mode 100644 apps/client-example/src/config/blockchain.ts delete mode 100644 apps/client-example/src/config/payment.config.ts delete mode 100644 apps/client-example/src/contexts/AuthContext.tsx delete mode 100644 apps/client-example/src/contexts/NotificationContext.tsx delete mode 100644 apps/client-example/src/contexts/ThemeContext.tsx delete mode 100644 apps/client-example/src/hooks/useMarketSocket.ts delete mode 100644 apps/client-example/src/hooks/useStorage.ts delete mode 100644 apps/client-example/src/hooks/useTradingDemo.ts delete mode 100644 apps/client-example/src/i18n/config.ts delete mode 100644 apps/client-example/src/i18n/request.ts delete mode 100644 apps/client-example/src/lib/api-base-url.utils.ts delete mode 100644 apps/client-example/src/lib/auth.service.ts delete mode 100644 apps/client-example/src/lib/blog-media.service.ts delete mode 100644 apps/client-example/src/lib/blog.service.ts delete mode 100644 apps/client-example/src/lib/category.service.ts delete mode 100644 apps/client-example/src/lib/challenge.service.ts delete mode 100644 apps/client-example/src/lib/image-proxy.ts delete mode 100644 apps/client-example/src/lib/market-data.service.ts delete mode 100644 apps/client-example/src/lib/nft.service.ts delete mode 100644 apps/client-example/src/lib/organization.service.ts delete mode 100644 apps/client-example/src/lib/ppoint.service.ts delete mode 100644 apps/client-example/src/lib/schema-generators.ts delete mode 100644 apps/client-example/src/lib/storage-url.utils.ts delete mode 100644 apps/client-example/src/lib/storage.service.ts delete mode 100644 apps/client-example/src/lib/submission.service.ts delete mode 100644 apps/client-example/src/lib/user.service.ts delete mode 100644 apps/client-example/src/lib/utils.ts delete mode 100644 apps/client-example/src/messages/en.json delete mode 100644 apps/client-example/src/messages/vi.json delete mode 100644 apps/client-example/src/middleware.ts delete mode 100644 apps/client-example/src/types/auth.ts delete mode 100644 apps/client-example/src/types/nft.ts delete mode 100644 apps/client-example/src/types/storage.ts delete mode 100644 apps/client-example/tailwind.config.js delete mode 100644 apps/client-example/test/cookies.txt delete mode 100644 apps/client-example/test/debug-env.js delete mode 100644 apps/client-example/test/test-admin-ppoint-dashboard.js delete mode 100644 apps/client-example/test/test-pagination-ui.js delete mode 100644 apps/client-example/test/test-ppoint-data-fix.js delete mode 100644 apps/client-example/test/test-submission-token.js delete mode 100644 apps/client-example/tsconfig.json diff --git a/apps/client-example/.eslintrc.js b/apps/client-example/.eslintrc.js deleted file mode 100644 index 5a58b88a..00000000 --- a/apps/client-example/.eslintrc.js +++ /dev/null @@ -1,6 +0,0 @@ -module.exports = { - extends: ['next/core-web-vitals'], - rules: { - // Add any custom rules here - } -}; diff --git a/apps/client-example/.eslintrc.json b/apps/client-example/.eslintrc.json deleted file mode 100644 index 8de3f112..00000000 --- a/apps/client-example/.eslintrc.json +++ /dev/null @@ -1,98 +0,0 @@ -{ - "extends": [ - "next/core-web-vitals", - "next", - "@typescript-eslint/recommended" - ], - "parser": "@typescript-eslint/parser", - "parserOptions": { - "ecmaVersion": "latest", - "sourceType": "module", - "ecmaFeatures": { - "jsx": true - }, - "project": "./tsconfig.json" - }, - "plugins": [ - "@typescript-eslint", - "react", - "react-hooks" - ], - "rules": { - // TypeScript rules - "@typescript-eslint/no-unused-vars": [ - "error", - { - "argsIgnorePattern": "^_", - "varsIgnorePattern": "^_" - } - ], - "@typescript-eslint/no-explicit-any": "warn", - "@typescript-eslint/explicit-function-return-type": "off", - "@typescript-eslint/explicit-module-boundary-types": "off", - "@typescript-eslint/no-empty-function": "warn", - "@typescript-eslint/prefer-const": "error", - "@typescript-eslint/no-inferrable-types": "off", - - // React rules - "react/react-in-jsx-scope": "off", - "react/prop-types": "off", - "react/no-unescaped-entities": "warn", - "react/jsx-uses-react": "off", - "react/jsx-uses-vars": "error", - "react/jsx-key": "error", - "react/no-array-index-key": "warn", - "react/self-closing-comp": ["error", { - "component": true, - "html": true - }], - - // React Hooks rules - "react-hooks/rules-of-hooks": "error", - "react-hooks/exhaustive-deps": "warn", - - // General JavaScript rules - "no-console": ["warn", { "allow": ["warn", "error"] }], - "no-debugger": "warn", - "no-unused-vars": "off", - "prefer-const": "error", - "no-var": "error", - "eqeqeq": ["error", "always"], - "curly": ["error", "all"], - - // Import rules (basic) - "import/no-duplicates": "error", - "import/order": [ - "error", - { - "groups": [ - "builtin", - "external", - "internal", - "parent", - "sibling", - "index" - ], - "newlines-between": "always" - } - ] - }, - "settings": { - "react": { - "version": "detect" - } - }, - "env": { - "browser": true, - "es2022": true, - "node": true - }, - "ignorePatterns": [ - "node_modules/", - ".next/", - "out/", - "build/", - "dist/", - "*.config.js" - ] -} \ No newline at end of file diff --git a/apps/client-example/.gitignore b/apps/client-example/.gitignore deleted file mode 100644 index 5ef6a520..00000000 --- a/apps/client-example/.gitignore +++ /dev/null @@ -1,41 +0,0 @@ -# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. - -# dependencies -/node_modules -/.pnp -.pnp.* -.yarn/* -!.yarn/patches -!.yarn/plugins -!.yarn/releases -!.yarn/versions - -# testing -/coverage - -# next.js -/.next/ -/out/ - -# production -/build - -# misc -.DS_Store -*.pem - -# debug -npm-debug.log* -yarn-debug.log* -yarn-error.log* -.pnpm-debug.log* - -# env files (can opt-in for committing if needed) -.env* - -# vercel -.vercel - -# typescript -*.tsbuildinfo -next-env.d.ts diff --git a/apps/client-example/.npmrc b/apps/client-example/.npmrc deleted file mode 100644 index 58efb11b..00000000 --- a/apps/client-example/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -install-strategy=nested - diff --git a/apps/client-example/.prettierrc b/apps/client-example/.prettierrc deleted file mode 100644 index 692d5909..00000000 --- a/apps/client-example/.prettierrc +++ /dev/null @@ -1,13 +0,0 @@ -{ - "semi": true, - "trailingComma": "none", - "singleQuote": true, - "printWidth": 100, - "tabWidth": 2, - "useTabs": false, - "bracketSpacing": true, - "arrowParens": "always", - "endOfLine": "lf", - "jsxSingleQuote": true, - "bracketSameLine": false -} diff --git a/apps/client-example/README.md b/apps/client-example/README.md deleted file mode 100644 index c45e13ef..00000000 --- a/apps/client-example/README.md +++ /dev/null @@ -1,264 +0,0 @@ -# 🚀 Enterprise Microservice Client - -Ứng dụng frontend NextJS cho hệ thống Microservice Enterprise với Advanced RBAC. - -## 🎯 Tính năng - -- ✅ **Authentication & Authorization** - Đăng nhập/đăng ký với JWT -- ✅ **Advanced RBAC** - Role-based access control với permissions chi tiết -- ✅ **Modern UI/UX** - Thiết kế responsive với Tailwind CSS -- ✅ **Type Safety** - TypeScript với type definitions đầy đủ -- ✅ **Real-time Updates** - Context-based state management -- ✅ **Error Handling** - Error boundaries và user-friendly messages - -## 📋 Yêu cầu - -- **Node.js** >= 18.0.0 -- **npm** hoặc **yarn** -- **Auth Service** đang chạy trên port 7001 - -## 🚀 Cài đặt và chạy - -### 1. Cài đặt dependencies - -```bash -npm install -# hoặc -yarn install -``` - -### 2. Cấu hình environment - -Tạo file `.env.local`: - -```bash -# Auth Service URL -NEXT_PUBLIC_AUTH_SERVICE_URL=http://localhost:7001 - -# Client Configuration -NEXT_PUBLIC_CLIENT_URL=http://localhost:3001 - -# API Configuration -NEXT_PUBLIC_API_VERSION=v1 - -# Development Settings -NODE_ENV=development -``` - -### 3. Chạy ứng dụng - -```bash -# Development mode -npm run dev -# hoặc -yarn dev - -# Production build -npm run build && npm run start -# hoặc -yarn build && yarn start -``` - -Ứng dụng sẽ chạy tại: **http://localhost:3001** - -## 🏗️ Cấu trúc thư mục - -``` -src/ -├── app/ # App Router pages -│ ├── auth/ # Authentication pages -│ │ ├── login/ # Login page -│ │ └── register/ # Register page -│ ├── dashboard/ # Protected dashboard -│ ├── layout.tsx # Root layout với AuthProvider -│ └── page.tsx # Home page -├── components/ -│ ├── auth/ # Auth components -│ │ ├── LoginForm.tsx # Login form -│ │ └── RegisterForm.tsx # Register form -│ └── ui/ # Reusable UI components -├── contexts/ -│ └── AuthContext.tsx # Authentication context -├── lib/ -│ └── auth.service.ts # Auth API service -├── types/ -│ └── auth.ts # Auth type definitions -└── styles/ - └── globals.css # Global styles -``` - -## 🔐 Authentication Flow - -### 1. **Đăng ký (Register)** -- Form validation (email, password strength, terms acceptance) -- Gửi request đến Auth Service `/api/auth/register` -- Auto login sau khi đăng ký thành công -- Redirect đến dashboard - -### 2. **Đăng nhập (Login)** -- Email/password validation -- JWT token storage (localStorage) -- Refresh token mechanism -- Remember me option - -### 3. **Authorization** -- Role-based access control -- Permission checking hooks -- Protected routes với middleware -- Auto redirect cho unauthorized access - -### 4. **Session Management** -- Auto token refresh -- Persistent login state -- Logout functionality -- Token expiration handling - -## 🎨 UI Components - -### Auth Components -- **LoginForm** - Form đăng nhập với validation -- **RegisterForm** - Form đăng ký với password strength -- **AuthProvider** - Context provider cho authentication state - -### UI Components -- **Button** - Component button với variants -- **Card** - Container component với styling -- **Toast** - Notification system với react-hot-toast - -## 🔧 Auth Service Integration - -### API Endpoints -``` -POST /api/auth/login # Đăng nhập -POST /api/auth/register # Đăng ký -POST /api/auth/logout # Đăng xuất -POST /api/auth/refresh # Refresh token -GET /api/auth/me # Current user info -PUT /api/auth/profile # Update profile -``` - -### Error Handling -- Network errors với retry mechanism -- Validation errors với field-level messages -- Auth errors với appropriate redirects -- User-friendly error messages trong tiếng Việt - -## 📱 Responsive Design - -- **Mobile First** - Thiết kế ưu tiên mobile -- **Breakpoints** - sm, md, lg, xl responsive breakpoints -- **Touch Friendly** - UI elements tối ưu cho touch -- **Performance** - Lazy loading và code splitting - -## 🛡️ Security Features - -- **CSRF Protection** - Cross-site request forgery protection -- **XSS Prevention** - Content Security Policy -- **Secure Storage** - Token storage với security best practices -- **Input Validation** - Client-side validation cho security -- **Rate Limiting** - Client-side rate limiting - -## 🧪 Development - -### Commands -```bash -npm run dev # Development server -npm run build # Production build -npm run start # Production server -npm run lint # ESLint checking -npm run type-check # TypeScript checking -``` - -### Development Notes -- Hot reload enabled cho development -- TypeScript strict mode -- ESLint với Next.js recommended rules -- Prettier cho code formatting - -## 🔗 Integration với Services - -### Auth Service (Port 7001) -- Authentication endpoints -- User management -- RBAC permissions -- Session handling - -### Future Services -- **User Service** (Port 7002) - User profiles & management -- **Order Service** (Port 7003) - Order processing -- **API Gateway** - Service orchestration - -## 📖 Usage Examples - -### Protected Component -```tsx -import { withAuth } from '@/contexts/AuthContext'; - -function ProtectedComponent() { - return
Protected content
; -} - -export default withAuth(ProtectedComponent); -``` - -### Permission Checking -```tsx -import { usePermissions } from '@/contexts/AuthContext'; - -function AdminPanel() { - const { hasPermission, hasRole } = usePermissions(); - - if (!hasRole('ADMIN')) { - return
Access denied
; - } - - return
Admin content
; -} -``` - -### Auth State -```tsx -import { useAuth } from '@/contexts/AuthContext'; - -function UserProfile() { - const { user, logout, loading } = useAuth(); - - if (loading) return
Loading...
; - if (!user) return
Not authenticated
; - - return ( -
-

Welcome {user.firstName}!

- -
- ); -} -``` - -## 🚀 Deployment - -### Environment Variables -```bash -NEXT_PUBLIC_AUTH_SERVICE_URL=https://auth.yourdomain.com -NEXT_PUBLIC_CLIENT_URL=https://app.yourdomain.com -NODE_ENV=production -``` - -### Build & Deploy -```bash -npm run build -npm run start -``` - -## 📞 Support - -- **Documentation**: `/docs` trong project root -- **API Reference**: Auth Service documentation -- **Issues**: GitHub issues cho bug reports -- **Enterprise Support**: Liên hệ team development - ---- - -**🎉 Happy Coding!** - -Hệ thống Enterprise Microservice với Advanced RBAC sẵn sàng phục vụ 10+ triệu users! 🚀 diff --git a/apps/client-example/docs/ALL_FIXES_COMPLETE.md b/apps/client-example/docs/ALL_FIXES_COMPLETE.md deleted file mode 100644 index bb57a49b..00000000 --- a/apps/client-example/docs/ALL_FIXES_COMPLETE.md +++ /dev/null @@ -1,226 +0,0 @@ -# 🎉 All Client Issues Fixed - Complete Summary - -## ✅ Issues Resolved: - -### 1. **Favicon 500 Error** ✅ FIXED -``` -❌ Before: GET /favicon.ico → 500 Internal Server Error -✅ After: GET /favicon.svg → 200 OK -``` - -**Changes:** -- Xóa invalid `favicon.ico` files -- Dùng `public/favicon.svg` -- Updated `manifest.json` icon purpose - ---- - -### 2. **Console Logs Cleanup** ✅ FIXED -**Removed 56 debug logs from:** -- `AuthContext.tsx` - 34 logs -- `auth.service.ts` - 5 logs -- `blog.service.ts` - 16 logs -- `NFTSocialDashboard.tsx` - 1 log - -``` -✅ Clean console -✅ Production ready -``` - ---- - -### 3. **Hydration Warning** ✅ FIXED -``` -❌ Before: Warning: Extra attributes from the server: class -✅ After: No warnings -``` - -**Root Cause:** Nested `` tags từ 2 layouts - -**Fix:** -```typescript -// app/layout.tsx - No HTML tags -export default function RootLayout({ children }) { - return children; -} - -// app/[locale]/layout.tsx - Has HTML structure - - -``` - ---- - -### 4. **Flash of White Content** ✅ FIXED -``` -❌ Before: White flash khi load page -✅ After: Instant dark mode -``` - -**Solution:** -```typescript -// Server: Default dark - - - -// ThemeProvider: Only modify if light -if (savedTheme === 'light') { - document.documentElement.classList.remove('dark'); -} -``` - ---- - -### 5. **next-intl Deprecation Warning** ✅ FIXED -``` -❌ Before: locale parameter deprecated -✅ After: Using await requestLocale -``` - -**Updated:** -```typescript -// i18n/request.ts -export default getRequestConfig(async ({ requestLocale }) => { - const locale = await requestLocale; - // ... -}); -``` - ---- - -### 6. **Metadata Deprecation Warnings** ✅ FIXED -``` -❌ Before: colorScheme/themeColor in metadata export -✅ After: Moved to viewport export -``` - -**Updated:** -```typescript -// app/[locale]/layout.tsx -export const viewport = { - width: 'device-width', - initialScale: 1, - colorScheme: 'dark light', - themeColor: [ - { media: '(prefers-color-scheme: light)', color: '#ffffff' }, - { media: '(prefers-color-scheme: dark)', color: '#111827' }, - ], -}; -``` - ---- - -### 7. **metadataBase Warning** ✅ FIXED -``` -❌ Before: No metadataBase set -✅ After: Using environment variable -``` - -**Updated:** -```typescript -// app/[locale]/layout.tsx -export async function generateMetadata() { - return { - metadataBase: new URL(process.env.NEXT_PUBLIC_SITE_URL || 'https://nextvision.ai'), - // ... - }; -} -``` - ---- - -## 📊 Final Status: - -| Issue | Status | Impact | -|-------|--------|--------| -| Favicon 500 | ✅ Fixed | No errors | -| Console Logs | ✅ Cleaned | Production ready | -| Hydration Warning | ✅ Fixed | No warnings | -| Flash (FOUC) | ✅ Fixed | Instant dark | -| next-intl Deprecation | ✅ Fixed | Future-proof | -| Metadata Deprecation | ✅ Fixed | NextJS 14+ compliant | -| metadataBase Warning | ✅ Fixed | SEO optimized | -| Manifest Warning | ✅ Fixed | PWA ready | - ---- - -## 🧪 Test Results: - -```bash -✅ No 500 errors -✅ No console logs -✅ No hydration warnings -✅ No flash on load -✅ No next-intl deprecation warnings -✅ No metadata deprecation warnings -✅ No metadataBase warnings -✅ All linter checks pass -✅ Dark mode instant -✅ Theme switching smooth -✅ SEO metadata complete -``` - ---- - -## 📝 Files Modified: - -``` -client/ -├── public/ -│ └── manifest.json ✅ Fixed icon purpose -├── src/ -│ ├── app/ -│ │ ├── layout.tsx ✅ Return children only -│ │ ├── [locale]/layout.tsx ✅ Dark class + inline styles -│ │ └── globals.css ✅ Optimized dark styles -│ ├── i18n/ -│ │ └── request.ts ✅ Use requestLocale -│ ├── contexts/ -│ │ ├── AuthContext.tsx ✅ Removed logs -│ │ └── ThemeContext.tsx ✅ Smart theme init -│ └── lib/ -│ ├── auth.service.ts ✅ Removed logs -│ └── blog.service.ts ✅ Removed logs -``` - ---- - -## 🎯 Remaining Warnings (IGNORE): - -```javascript -// Browser Extension Warnings - Not our code! -injected.js:1 Provider initialised (TronLink Wallet) -injected.js:1 TronLink initiated -``` -→ Từ crypto wallet extensions, không ảnh hưởng app - ---- - -## 🚀 Production Ready! - -**Test Command:** -```bash -# Hard reload -Cmd + Shift + R (Mac) -Ctrl + Shift + R (Windows) -``` - -**Expected:** -``` -✅ Instant dark mode -✅ Clean console -✅ No warnings -✅ Smooth experience -``` - ---- - -**🎉 ALL ISSUES RESOLVED!** - -Application bây giờ: -- ✅ Error-free -- ✅ Warning-free (except extensions) -- ✅ Clean code -- ✅ Production ready -- ✅ Smooth UX - diff --git a/apps/client-example/docs/FAVICON_SIMPLE_FIX.md b/apps/client-example/docs/FAVICON_SIMPLE_FIX.md deleted file mode 100644 index 465d18ad..00000000 --- a/apps/client-example/docs/FAVICON_SIMPLE_FIX.md +++ /dev/null @@ -1,52 +0,0 @@ -# 🔧 Favicon 500 Error - Simple Fix - -## ❌ Vấn đề: -``` -GET /favicon.ico → 500 Internal Server Error -``` - -## ✅ Nguyên nhân: -File `favicon.ico` chứa **SVG content** thay vì format ICO đúng. - -## 🛠️ Giải pháp (Cực đơn giản): - -### 1. Xóa file ICO bị lỗi: -```bash -# Đã xóa: -- client/public/favicon.ico -- client/src/app/favicon.ico -``` - -### 2. Dùng SVG từ public/: -``` -client/public/ -├── favicon.svg ✅ NextJS tự serve -└── apple-touch-icon.svg ✅ NextJS tự serve -``` - -### 3. Update metadata: -```typescript -// src/app/layout.tsx -export const metadata = { - icons: { - icon: '/favicon.svg', - apple: '/apple-touch-icon.svg', - }, -}; -``` - -## ✅ Kết quả: -``` -✅ favicon.svg: 200 OK -✅ apple-touch-icon.svg: 200 OK -✅ Homepage: 200 OK -``` - -**Xong!** Không cần route handlers, không cần config phức tạp. - -NextJS tự động serve static files từ `public/`. SVG modern browsers đều support. - ---- - -**🎉 Fixed! Clear cache (Cmd+Shift+R) và reload để see favicon.** - diff --git a/apps/client-example/docs/FILE_GRID_NAVIGATION.md b/apps/client-example/docs/FILE_GRID_NAVIGATION.md deleted file mode 100644 index 6ebc91ee..00000000 --- a/apps/client-example/docs/FILE_GRID_NAVIGATION.md +++ /dev/null @@ -1,630 +0,0 @@ -# File Grid Navigation Documentation - -## 📊 Tổng Quan - -**Date:** October 15, 2025 -**Status:** ✅ Completed -**Component:** `FileGrid` + `FileGridNavigation` - -## 🎯 Mục Đích - -Thêm pagination/navigation controls vào `FileGrid` component để hiển thị và điều hướng qua nhiều trang files. - -## 📁 Components - -### 1. **FileGridNavigation.tsx** -**Purpose:** Pagination controls với page numbers và navigation buttons - -**Features:** -- Previous/Next buttons -- Page number buttons (smart ellipsis) -- Total items display -- Jump to page input (desktop) -- Responsive design (mobile-friendly) -- Loading state support - -**Props:** -```typescript -interface FileGridNavigationProps { - currentPage: number; // Trang hiện tại (1-based) - totalPages: number; // Tổng số trang - totalItems: number; // Tổng số items - itemsPerPage: number; // Số items mỗi trang - onPageChange: (page: number) => void; // Page change handler - loading?: boolean; // Loading state - className?: string; // Custom CSS classes -} -``` - -**Lines:** ~165 lines - -### 2. **FileGrid.tsx** (Updated) -**Purpose:** Display files với optional pagination - -**New Props:** -```typescript -interface FileGridProps { - // ... existing props ... - - // Pagination props (optional) - showPagination?: boolean; // Enable/disable pagination - currentPage?: number; // Current page number - totalPages?: number; // Total pages - totalItems?: number; // Total items count - itemsPerPage?: number; // Items per page - onPageChange?: (page: number) => void; // Page change handler - loading?: boolean; // Loading state -} -``` - -## 💡 Usage Examples - -### Example 1: Basic Usage (No Pagination) - -```tsx -import { FileGrid } from '@/components/storage'; - -function MyStoragePage() { - const [files, setFiles] = useState([]); - - return ( - console.log('Preview:', file)} - // No pagination props = no pagination UI - /> - ); -} -``` - -### Example 2: With Pagination (Client-Side) - -```tsx -import { FileGrid } from '@/components/storage'; -import { useState, useMemo } from 'react'; - -function MyStoragePageWithPagination() { - const [allFiles, setAllFiles] = useState([]); - const [currentPage, setCurrentPage] = useState(1); - const itemsPerPage = 20; - - // Calculate pagination - const totalPages = Math.ceil(allFiles.length / itemsPerPage); - const paginatedFiles = useMemo(() => { - const startIndex = (currentPage - 1) * itemsPerPage; - const endIndex = startIndex + itemsPerPage; - return allFiles.slice(startIndex, endIndex); - }, [allFiles, currentPage, itemsPerPage]); - - return ( - console.log('Preview:', file)} - /> - ); -} -``` - -### Example 3: With Pagination (Server-Side) - -```tsx -import { FileGrid } from '@/components/storage'; -import { useState, useEffect } from 'react'; - -function MyServerPaginatedStorage() { - const [files, setFiles] = useState([]); - const [currentPage, setCurrentPage] = useState(1); - const [totalItems, setTotalItems] = useState(0); - const [loading, setLoading] = useState(false); - const itemsPerPage = 20; - - const fetchFiles = async (page: number) => { - setLoading(true); - try { - const response = await storageService.getFiles({ - page, - limit: itemsPerPage - }); - - if (response.success) { - setFiles(response.data.files); - setTotalItems(response.data.meta.totalItems); - } - } catch (error) { - console.error('Failed to fetch files:', error); - } finally { - setLoading(false); - } - }; - - useEffect(() => { - fetchFiles(currentPage); - }, [currentPage]); - - const handlePageChange = (page: number) => { - setCurrentPage(page); - // Scroll to top when page changes - window.scrollTo({ top: 0, behavior: 'smooth' }); - }; - - const totalPages = Math.ceil(totalItems / itemsPerPage); - - return ( - console.log('Preview:', file)} - /> - ); -} -``` - -### Example 4: Complete Integration with StoragePageContent - -```tsx -// In your storage page -import { useState, useCallback } from 'react'; -import { StoragePageContent } from '@/components/storage'; - -function StoragePage() { - const [files, setFiles] = useState([]); - const [currentPage, setCurrentPage] = useState(1); - const [totalItems, setTotalItems] = useState(0); - const itemsPerPage = 24; // 4x6 grid - - // Fetch files with pagination - const fetchFiles = useCallback(async (page: number) => { - const response = await storageService.getFiles({ - page, - limit: itemsPerPage, - folderId: currentFolder?.id - }); - - if (response.success) { - setFiles(response.data.files); - setTotalItems(response.data.meta.totalItems); - } - }, [currentFolder]); - - useEffect(() => { - fetchFiles(currentPage); - }, [currentPage, fetchFiles]); - - return ( - - ); -} -``` - -## 🎨 Navigation Features - -### 1. **Smart Page Numbers** -- Shows up to 7 page buttons -- Uses ellipsis (...) for large page ranges -- Always shows first and last page -- Shows pages around current page - -**Example displays:** -- **5 pages:** `1 2 3 4 5` -- **10 pages, current=1:** `1 2 3 ... 10` -- **10 pages, current=5:** `1 ... 4 5 6 ... 10` -- **10 pages, current=10:** `1 ... 8 9 10` - -### 2. **Responsive Design** -- **Desktop:** Full pagination with page numbers -- **Mobile:** Simple "Page X / Y" indicator -- **Tablet:** Medium view with essential controls - -### 3. **Accessibility** -- Keyboard navigation support -- Disabled state for loading -- Clear visual feedback -- ARIA labels for screen readers - -### 4. **Loading State** -- Disabled controls during loading -- Visual feedback -- Prevents double-clicks - -## 🔧 Integration Steps - -### Step 1: Update FileGrid Props - -```tsx - -``` - -### Step 2: Implement Page Change Handler - -```tsx -const handlePageChange = (newPage: number) => { - // Update current page - setCurrentPage(newPage); - - // Optional: Scroll to top - window.scrollTo({ top: 0, behavior: 'smooth' }); - - // Optional: Fetch new data (server-side) - fetchFiles(newPage); -}; -``` - -### Step 3: Calculate Pagination (Client-Side) - -```tsx -const itemsPerPage = 20; -const totalPages = Math.ceil(allFiles.length / itemsPerPage); - -const paginatedFiles = useMemo(() => { - const start = (currentPage - 1) * itemsPerPage; - const end = start + itemsPerPage; - return allFiles.slice(start, end); -}, [allFiles, currentPage, itemsPerPage]); -``` - -## 📊 Pagination Strategies - -### 1. **Client-Side Pagination** -**Pros:** -- Fast page switching -- No server requests -- Works offline - -**Cons:** -- All data loaded upfront -- Memory intensive for large datasets - -**Best for:** -- < 1000 files -- Fast local filtering -- Offline-first apps - -**Implementation:** -```tsx -const paginatedFiles = allFiles.slice( - (page - 1) * itemsPerPage, - page * itemsPerPage -); -``` - -### 2. **Server-Side Pagination** -**Pros:** -- Memory efficient -- Handles large datasets -- Real-time data - -**Cons:** -- Network requests per page -- Slower page switching - -**Best for:** -- > 1000 files -- Real-time updates -- Large file libraries - -**Implementation:** -```tsx -const fetchFiles = async (page: number) => { - const response = await api.getFiles({ - page, - limit: itemsPerPage - }); - return response.data; -}; -``` - -### 3. **Hybrid Pagination** -**Pros:** -- Best of both worlds -- Smart caching -- Good UX - -**Implementation:** -```tsx -const [cache, setCache] = useState>(new Map()); - -const fetchPage = async (page: number) => { - if (cache.has(page)) { - return cache.get(page)!; - } - - const data = await api.getFiles({ page, limit: itemsPerPage }); - setCache(prev => new Map(prev).set(page, data)); - return data; -}; -``` - -## 🎛️ Customization Options - -### Items Per Page Options - -```tsx -const ITEMS_PER_PAGE_OPTIONS = [12, 24, 48, 96]; - -function MyStorage() { - const [itemsPerPage, setItemsPerPage] = useState(24); - - return ( - <> - - - - - ); -} -``` - -### Custom Navigation Style - -```tsx - -``` - -## 🚀 Performance Tips - -### 1. **Optimize Re-renders** -```tsx -const paginatedFiles = useMemo(() => { - return files.slice((page - 1) * limit, page * limit); -}, [files, page, limit]); -``` - -### 2. **Debounce Page Changes** -```tsx -const debouncedPageChange = useMemo( - () => debounce((page: number) => { - setCurrentPage(page); - }, 300), - [] -); -``` - -### 3. **Virtual Scrolling (Alternative)** -For very large datasets, consider virtual scrolling instead: -```tsx -import { FixedSizeGrid } from 'react-window'; - - - {({ columnIndex, rowIndex, style }) => ( -
- -
- )} -
-``` - -## 📱 Mobile Optimizations - -### Touch-Friendly Buttons -- Minimum 44x44px touch targets -- Adequate spacing between buttons -- Clear visual feedback - -### Simplified Mobile View -- Shows "Page X / Y" instead of all page numbers -- Larger prev/next buttons -- Swipe gestures support (future enhancement) - -## 🔄 State Management - -### Recommended Pattern - -```tsx -function StorageWithPagination() { - // Pagination state - const [currentPage, setCurrentPage] = useState(1); - const [itemsPerPage, setItemsPerPage] = useState(24); - - // Data state - const [allFiles, setAllFiles] = useState([]); - const [loading, setLoading] = useState(false); - - // Computed values - const totalPages = Math.ceil(allFiles.length / itemsPerPage); - const paginatedFiles = useMemo(() => { - const start = (currentPage - 1) * itemsPerPage; - return allFiles.slice(start, start + itemsPerPage); - }, [allFiles, currentPage, itemsPerPage]); - - // Reset to page 1 when filters change - useEffect(() => { - setCurrentPage(1); - }, [searchQuery, sortBy, folderId]); - - return ( - itemsPerPage} - currentPage={currentPage} - totalPages={totalPages} - totalItems={allFiles.length} - itemsPerPage={itemsPerPage} - onPageChange={setCurrentPage} - loading={loading} - /> - ); -} -``` - -## 🎨 UI/UX Highlights - -### Visual States - -1. **Active Page** - - Blue background - - White text - - Clear visual distinction - -2. **Hover State** - - Gray background - - Smooth transition - - Cursor pointer - -3. **Disabled State** - - Gray text - - No hover effect - - Cursor not-allowed - -4. **Loading State** - - Disabled controls - - Reduced opacity - - Loading indicator (if needed) - -### Smart Features - -1. **Automatic Ellipsis** - - Shows `...` for skipped pages - - Always visible: first, last, and nearby pages - -2. **Jump to Page** (Desktop) - - Quick navigation to specific page - - Input validation - - Only shows for 5+ pages - -3. **Responsive Text** - - Desktop: "Hiển thị 1 - 20 trong tổng số 150 files" - - Mobile: "1 / 8" - -## 🧪 Testing Checklist - -- [x] Component renders without pagination -- [x] Component renders with pagination -- [x] Previous button works -- [x] Next button works -- [x] Page number buttons work -- [x] Ellipsis appears correctly -- [x] Jump to page works (desktop) -- [x] Mobile view displays correctly -- [x] Loading state disables controls -- [x] First/last page edge cases handled -- [ ] User testing in browser (pending) - -## 📚 Related Files - -- `client/src/components/storage/FileGrid.tsx` - Main grid component -- `client/src/components/storage/FileGridNavigation.tsx` - Navigation component -- `client/src/components/storage/index.ts` - Exports -- `client/src/app/[locale]/dashboard/storage/page.tsx` - Usage example - -## 🔍 Code Quality - -| Metric | Value | -|--------|-------| -| TypeScript | ✅ Fully typed | -| Linter | ✅ No errors | -| Accessibility | ✅ ARIA labels | -| Responsive | ✅ Mobile-friendly | -| Performance | ✅ Optimized | - -## 🎯 Future Enhancements - -1. **URL State Sync** - ```tsx - const searchParams = useSearchParams(); - const page = Number(searchParams.get('page')) || 1; - - const handlePageChange = (newPage: number) => { - router.push(`?page=${newPage}`); - }; - ``` - -2. **Keyboard Navigation** - ```tsx - useEffect(() => { - const handleKey = (e: KeyboardEvent) => { - if (e.key === 'ArrowLeft') handlePageChange(currentPage - 1); - if (e.key === 'ArrowRight') handlePageChange(currentPage + 1); - }; - window.addEventListener('keydown', handleKey); - return () => window.removeEventListener('keydown', handleKey); - }, [currentPage]); - ``` - -3. **Infinite Scroll Option** - ```tsx - const { ref, inView } = useInView(); - - useEffect(() => { - if (inView && hasMore && !loading) { - loadMore(); - } - }, [inView, hasMore, loading]); - ``` - -4. **Per-Page Selector** - ```tsx - - ``` - -## 📅 Change Log - -- **2025-10-15** - Initial implementation - - Created `FileGridNavigation` component - - Updated `FileGrid` with pagination support - - Added comprehensive documentation - - No linter errors - - No TypeScript errors - - Fully responsive design - - Smart ellipsis algorithm - - Jump to page feature (desktop) - diff --git a/apps/client-example/docs/FILE_GRID_PAGINATION_EXAMPLE.md b/apps/client-example/docs/FILE_GRID_PAGINATION_EXAMPLE.md deleted file mode 100644 index 99df37d5..00000000 --- a/apps/client-example/docs/FILE_GRID_PAGINATION_EXAMPLE.md +++ /dev/null @@ -1,350 +0,0 @@ -# File Grid Pagination - Example Implementation - -## 🎯 Ví Dụ Thực Tế - -### Example 1: Update Storage Page với Client-Side Pagination - -```tsx -// client/src/app/[locale]/dashboard/storage/page.tsx - -'use client'; - -import React, { useState, useCallback, useEffect, useMemo } from 'react'; -import { useTranslations } from 'next-intl'; -import { useStorage } from '@/hooks/useStorage'; -import { FileResponse, FolderResponse } from '@/types/storage'; -import { - buildBreadcrumbs, - filterAndSortFiles, - filterFolders, - StoragePageHeader, - StoragePageSidebar, - StoragePageContent, - StoragePageModals -} from '@/components/storage'; - -export default function StoragePage() { - const t = useTranslations('Storage'); - - const { - files, - folders, - currentFolder, - quota, - loading, - error, - // ... other hooks - } = useStorage(); - - // ============================================================================ - // PAGINATION STATE (NEW) - // ============================================================================ - - const [currentPage, setCurrentPage] = useState(1); - const [itemsPerPage, setItemsPerPage] = useState(24); // 4x6 grid - - // Reset to page 1 when filters change - useEffect(() => { - setCurrentPage(1); - }, [searchQuery, sortBy, currentFolder]); - - // ============================================================================ - // COMPUTE PAGINATED DATA - // ============================================================================ - - // Filter and sort files first - const filteredFiles = filterAndSortFiles( - files, - searchQuery, - sortBy, - sortOrder, - isAdvancedSearchActive, - advancedSearchResults - ); - - // Calculate pagination - const totalPages = Math.ceil(filteredFiles.length / itemsPerPage); - const totalItems = filteredFiles.length; - - // Get current page files - const paginatedFiles = useMemo(() => { - const startIndex = (currentPage - 1) * itemsPerPage; - const endIndex = startIndex + itemsPerPage; - return filteredFiles.slice(startIndex, endIndex); - }, [filteredFiles, currentPage, itemsPerPage]); - - // ============================================================================ - // PAGE CHANGE HANDLER - // ============================================================================ - - const handlePageChange = useCallback((newPage: number) => { - setCurrentPage(newPage); - - // Optional: Scroll to top when page changes - window.scrollTo({ - top: 0, - behavior: 'smooth' - }); - }, []); - - // ============================================================================ - // RENDER - // ============================================================================ - - return ( - <> - - -
-
- - -
- setSortOrder(prev => prev === 'asc' ? 'desc' : 'asc')} - onShowAdvancedSearch={() => setShowAdvancedSearch(true)} - onShowDuplicateManager={() => setShowDuplicateManager(true)} - isAdvancedSearchActive={isAdvancedSearchActive} - advancedSearchResults={advancedSearchResults} - files={files} - folders={folders} - currentFolder={currentFolder} - filteredFiles={paginatedFiles} // ← Use paginated files - filteredFolders={filterFolders(folders, searchQuery)} - onClearAdvancedSearch={() => { - setIsAdvancedSearchActive(false); - setAdvancedSearchResults([]); - }} - onClearSearch={() => setSearchQuery('')} - onUploadComplete={handleUploadComplete} - selectedFiles={selectedFiles} - onSelectionChange={setSelectedFiles} - onSelectionClear={() => setSelectedFiles([])} - onOperationComplete={refreshData} - viewMode={viewMode} - onFileDelete={handleFileDelete} - onFileShare={handleFileShare} - onFilePreview={handleFilePreview} - onFileVersioning={handleFileVersioning} - loading={loading} - error={error} - - // ← NEW: Pagination props - showPagination={filteredFiles.length > itemsPerPage} - currentPage={currentPage} - totalPages={totalPages} - totalItems={totalItems} - itemsPerPage={itemsPerPage} - onPageChange={handlePageChange} - - t={t} - /> -
-
-
- - - - ); -} -``` - ---- - -## 📊 Kết Quả - -### Với 150 files, itemsPerPage = 24: -- **Page 1:** Files 1-24 -- **Page 2:** Files 25-48 -- **Page 3:** Files 49-72 -- ... -- **Page 7:** Files 145-150 - -### Navigation hiển thị: -``` -[←] 1 2 3 ... 7 [→] (Page 1) -[←] 1 2 3 4 ... 7 [→] (Page 2) -[←] 1 ... 3 4 5 ... 7 [→] (Page 4) -[←] 1 ... 5 6 7 [→] (Page 7) -``` - ---- - -## 🎛️ Tuỳ Chỉnh Items Per Page - -```tsx -// Add items per page selector -function StoragePageHeader() { - return ( -
- {/* Existing controls */} - - {/* Items per page selector */} - -
- ); -} -``` - ---- - -## 🔄 Server-Side Pagination Example - -```tsx -export default function StoragePage() { - const [files, setFiles] = useState([]); - const [currentPage, setCurrentPage] = useState(1); - const [totalItems, setTotalItems] = useState(0); - const [loading, setLoading] = useState(false); - const itemsPerPage = 24; - - // Fetch files with pagination from server - const fetchFiles = useCallback(async (page: number) => { - setLoading(true); - try { - const { StorageService } = await import('@/lib/storage.service'); - const storageService = new StorageService(); - - const result = await storageService.getFiles({ - page, - limit: itemsPerPage, - folderId: currentFolder?.id, - sortBy, - sortOrder - }); - - if (result.success && result.data) { - setFiles(result.data.files); - setTotalItems(result.data.meta.totalItems); - } - } catch (error) { - console.error('Failed to fetch files:', error); - } finally { - setLoading(false); - } - }, [currentFolder, sortBy, sortOrder, itemsPerPage]); - - // Load files when page or filters change - useEffect(() => { - fetchFiles(currentPage); - }, [currentPage, fetchFiles]); - - // Handle page change - const handlePageChange = useCallback((newPage: number) => { - setCurrentPage(newPage); - window.scrollTo({ top: 0, behavior: 'smooth' }); - }, []); - - const totalPages = Math.ceil(totalItems / itemsPerPage); - - return ( - - ); -} -``` - ---- - -## 🎨 Visual Preview - -``` -┌─────────────────────────────────────────────────────────┐ -│ [File 1] [File 2] [File 3] [File 4] [File 5] [File 6] │ -│ [File 7] [File 8] [File 9] [File 10] [File 11] [...] │ -│ [File 13] [File 14] [File 15] [File 16] [File 17] ... │ -│ [File 19] [File 20] [File 21] [File 22] [File 23] ... │ -└─────────────────────────────────────────────────────────┘ - -┌─────────────────────────────────────────────────────────┐ -│ Hiển thị 1 - 24 trong tổng số 150 files │ -│ │ -│ [←] 1 [2] 3 4 ... 7 [→] [Đi đến: 1] │ -└─────────────────────────────────────────────────────────┘ -``` - ---- - -## ✅ Benefits - -1. **Better UX** - - Không load tất cả files cùng lúc - - Faster initial page load - - Smooth navigation - -2. **Performance** - - Reduced DOM nodes - - Faster rendering - - Lower memory usage - -3. **Scalability** - - Handles thousands of files - - Server-side ready - - Caching support - -4. **Accessibility** - - Keyboard navigation - - Screen reader friendly - - Clear visual feedback - ---- - -## 🧪 Testing Guide - -### Test Cases: - -1. **Basic Navigation** - - [ ] Click next → goes to page 2 - - [ ] Click previous → goes to page 1 - - [ ] Click page number → goes to that page - -2. **Edge Cases** - - [ ] First page: previous button disabled - - [ ] Last page: next button disabled - - [ ] Single page: no pagination shown - - [ ] Empty files: no pagination - -3. **Responsive** - - [ ] Desktop: full pagination UI - - [ ] Mobile: simplified "X / Y" display - - [ ] Tablet: medium view - -4. **Loading State** - - [ ] Controls disabled during loading - - [ ] Visual feedback shown - -5. **Integration** - - [ ] Works with search - - [ ] Works with sorting - - [ ] Works with folder navigation - - [ ] Resets to page 1 on filter change - diff --git a/apps/client-example/docs/FIXES_SUMMARY.md b/apps/client-example/docs/FIXES_SUMMARY.md deleted file mode 100644 index c3057466..00000000 --- a/apps/client-example/docs/FIXES_SUMMARY.md +++ /dev/null @@ -1,105 +0,0 @@ -# 🎉 Client Issues Fixed - Summary - -## ✅ Issues Resolved: - -### 1. **Favicon 500 Error** ✅ -**Problem:** `/favicon.ico` → 500 Internal Server Error - -**Solution:** -- Xóa invalid `favicon.ico` files (chứa SVG content) -- Dùng `public/favicon.svg` (NextJS tự serve) -- Update metadata: `icon: '/favicon.svg'` - -**Result:** -``` -✅ /favicon.svg → 200 OK -✅ /apple-touch-icon.svg → 200 OK -``` - ---- - -### 2. **Console Logs Cleanup** ✅ -**Problem:** 50+ console.log statements trong production code - -**Removed from:** -- `AuthContext.tsx` - 34 logs -- `auth.service.ts` - 5 logs -- `blog.service.ts` - 16 logs -- `NFTSocialDashboard.tsx` - 1 log -- `dashboard/page.tsx` - 6 logs - -**Result:** -``` -✅ Clean console -✅ No debug logs -✅ Production ready -``` - ---- - -### 3. **Hydration Warning** ✅ -**Problem:** -``` -Warning: Extra attributes from the server: class -at and tags -``` - -**Root Cause:** -- Server render: `` -- ThemeProvider modify class after mount -- → Mismatch → Warning - -**Solution:** -```typescript -// 1. Server: Always render with 'dark' - - -// 2. CSS: Default dark -html { @apply dark; } - -// 3. ThemeProvider: Only modify if savedTheme !== 'dark' -useEffect(() => { - const savedTheme = localStorage.getItem('theme'); - if (savedTheme === 'light') { - document.documentElement.classList.remove('dark'); - } - // If 'dark' or null, keep server-rendered class (no modify) -}, []); -``` - -**Result:** -``` -✅ No hydration warnings -✅ Server-client match -✅ Theme switching works -``` - ---- - -## 📊 Final Status: - -| Issue | Status | Files Changed | -|-------|--------|---------------| -| Favicon 500 | ✅ Fixed | 2 deleted, 1 updated | -| Console Logs | ✅ Cleaned | 5 files | -| Hydration Warning | ✅ Fixed | 3 files | - ---- - -## 🧪 Test Results: - -```bash -✅ No 500 errors -✅ No console logs -✅ No hydration warnings -✅ Dark mode works -✅ Theme switching works -✅ All linter checks pass -``` - ---- - -**🎉 All Issues Resolved!** - -**Test:** Clear cache (Cmd+Shift+R) và reload → Tất cả hoạt động perfect! - diff --git a/apps/client-example/docs/FOLDER_TREE_ALL_FILES_COLLAPSE.md b/apps/client-example/docs/FOLDER_TREE_ALL_FILES_COLLAPSE.md deleted file mode 100644 index 3184c923..00000000 --- a/apps/client-example/docs/FOLDER_TREE_ALL_FILES_COLLAPSE.md +++ /dev/null @@ -1,595 +0,0 @@ -# Folder Tree - "All Files" Collapse/Expand Feature - -## 📊 Tổng Quan - -**Date:** October 15, 2025 -**Status:** ✅ Completed -**Component:** `FolderTree.tsx` -**Lines:** 438 → 472 lines (+34 lines) - -## 🎯 Mục Đích - -Thêm tính năng **collapse/expand cho "All Files"** để ẩn/hiện toàn bộ folder tree. **Mặc định là collapsed** (ẩn folder tree) để UI gọn gàng hơn. - -## ✨ Features Đã Thêm - -### 1. **"All Files" Collapse/Expand Button** - -**Location:** Bên trái "All Files" label - -**Behavior:** -- **Mặc định:** ▶ (collapsed) - Folder tree ẨN -- **Click expand:** ▼ (expanded) - Folder tree HIỆN -- **Click collapse:** ▶ (collapsed) - Folder tree ẨN lại - -**Visual:** -``` -DEFAULT (Collapsed): -┌──────────────────────────────────────────┐ -│ Folders [5] [▲] [+ New] │ -├──────────────────────────────────────────┤ -│ ▶ 📁 All Files [5] │ ← Collapsed (no folders visible) -└──────────────────────────────────────────┘ - -Click [▶] to expand ↓ - -EXPANDED: -┌──────────────────────────────────────────┐ -│ Folders [5] [▼] [+ New] │ -├──────────────────────────────────────────┤ -│ ▼ 📂 All Files [5] │ ← Expanded -│ ▶ 📁 Documents [3] │ -│ ▶ 📁 Photos [12] │ -│ ▶ 📁 Videos [8] │ -│ ▶ 📁 Projects [25] │ -│ ▶ 📁 Archive [156] │ -└──────────────────────────────────────────┘ - -Click [▼] to collapse ↑ -``` - -### 2. **Folder Count Badge on "All Files"** - -Hiển thị số lượng folders ngay trên "All Files" row: - -``` -▶ 📁 All Files [5] - ▲ - └─ Số folders -``` - -### 3. **Auto-Expand on "New Folder"** - -Khi click nút **"New Folder"**, tự động expand "All Files" để user thấy input form. - -## 🔧 Technical Implementation - -### New State - -```typescript -const [isAllFilesExpanded, setIsAllFilesExpanded] = useState(false); // Mặc định collapsed -``` - -**Why `false` by default?** -- UI gọn gàng hơn -- User có thể focus vào "All Files" view trước -- Expand khi cần xem folder structure - -### Toggle Handler - -```typescript -const handleToggleAllFiles = useCallback(() => { - setIsAllFilesExpanded(prev => !prev); -}, []); -``` - -### Auto-Expand on Create Folder - -```typescript -const handleCreateFolder = useCallback((parentId?: string) => { - setCreatingFolder({ parentId }); - setNewFolderName(''); - - // Auto-expand "All Files" để hiển thị form - if (!isAllFilesExpanded) { - setIsAllFilesExpanded(true); - } -}, [isAllFilesExpanded]); -``` - -### UI Components - -#### "All Files" Row (Updated) - -```tsx -
- {/* Expand/Collapse Button */} - {folders.length > 0 && ( - - )} - - {/* Folder Icon */} -
handleFolderSelect(null)}> - ... -
- - {/* Label */} - handleFolderSelect(null)}> - All Files - - - {/* Folder count */} - {folders.length > 0 && ( - - {folders.length} - - )} -
-``` - -#### Conditional Folder Hierarchy - -```tsx -{/* Folder hierarchy - chỉ hiển thị khi expanded */} -{isAllFilesExpanded && rootFolders.map(folder => { - // ... render folder items -})} -``` - -#### Conditional Create/Edit Form - -```tsx -{/* Create/Edit Folder Input - chỉ hiển thị khi expanded */} -{isAllFilesExpanded && (creatingFolder || editingFolder) && ( -
- {/* ... folder input form */} -
-)} -``` - -## 📱 Visual States - -### State 1: Default Collapsed - -``` -┌──────────────────────────────────────────────────────┐ -│ Folders [5] [▲] [+ New] │ -├──────────────────────────────────────────────────────┤ -│ ▶ 📁 All Files [5] │ -│ │ -│ [No folders visible] │ -│ │ -└──────────────────────────────────────────────────────┘ - -✅ Clean UI -✅ Focus on "All Files" -✅ Quick overview of folder count (5) -``` - -### State 2: Expanded (Click ▶) - -``` -┌──────────────────────────────────────────────────────┐ -│ Folders [5] [▲] [+ New] │ -├──────────────────────────────────────────────────────┤ -│ ▼ 📂 All Files [5] │ -│ ▶ 📁 Documents [3] │ -│ ▶ 📁 Photos [12] │ -│ ▶ 📁 Videos [8] │ -│ ▶ 📁 Projects [25] │ -│ ▶ 📁 Archive [156] │ -└──────────────────────────────────────────────────────┘ - -✅ Full folder tree visible -✅ Can navigate to specific folders -✅ Can expand individual folders -``` - -### State 3: Auto-Expand on "New Folder" - -``` -User clicks [+ New] button → - -┌──────────────────────────────────────────────────────┐ -│ Folders [5] [▲] [+ New] │ -├──────────────────────────────────────────────────────┤ -│ ▼ 📂 All Files [5] │ ← Auto-expanded -│ ▶ 📁 Documents [3] │ -│ ▶ 📁 Photos [12] │ -│ ▶ 📁 Videos [8] │ -│ ┌────────────────────────────────────────┐ │ -│ │ Folder name: [____________] ✓ ✗ │ │ ← Input form visible -│ └────────────────────────────────────────┘ │ -└──────────────────────────────────────────────────────┘ - -✅ "All Files" auto-expanded -✅ Input form visible -✅ User can create folder immediately -``` - -## 🎯 Use Cases - -### 1. **Quick "All Files" View (Default)** - -Default collapsed state cho phép user: -- Xem tất cả files trong root -- Focus vào content, không bị distract bởi folder tree -- Quick access to "All Files" - -``` -Click "All Files" → View all files - → No folder filtering -``` - -### 2. **Navigate to Specific Folder** - -Khi cần vào folder cụ thể: -- Click ▶ expand "All Files" -- Chọn folder cần xem -- Click vào folder → Folder tree tự collapse lại (optional) - -``` -▶ All Files → Click → ▼ All Files - ▶ Documents → Click → View Documents -``` - -### 3. **Create New Folder** - -Khi click "New Folder": -- "All Files" tự động expand -- Input form xuất hiện -- Tạo folder ngay lập tức - -``` -Click [+ New] → ▼ All Files (auto-expanded) - [Folder name input] -``` - -### 4. **Clean UI Mode** - -Khi muốn UI sạch sẽ: -- Click ▼ collapse "All Files" -- Folder tree ẩn đi -- Chỉ thấy "All Files" row - -``` -▼ All Files → Click → ▶ All Files - ▶ Documents [Folders hidden] - ▶ Photos - ▶ Videos -``` - -## 🎨 UI/UX Details - -### Icon States - -| State | Icon | Arrow Direction | Action | -|-------|------|----------------|--------| -| **Collapsed** | ▶ | Right → | Click to expand | -| **Expanded** | ▼ | Down ↓ | Click to collapse | - -**CSS Transition:** -```css -.h-3 .w-3 .transition-transform { - transform: rotate(0deg); /* Collapsed: ▶ */ -} - -.rotate-90 { - transform: rotate(90deg); /* Expanded: ▼ (rotated 90°) */ -} -``` - -### Click Targets - -``` -┌──────────────────────────────────────────┐ -│ [▶] [📁] [All Files...........] [5] │ -│ ▲ ▲ ▲ ▲ │ -│ │ │ │ │ │ -│ │ │ │ └─ Folder count (read-only) -│ │ │ └─ Select "All Files" view -│ │ └─ Select "All Files" view -│ └─ Toggle expand/collapse -└──────────────────────────────────────────┘ - -Click Areas: -• [▶] button → Toggle expand/collapse (stops propagation) -• [📁] icon → Select "All Files" view -• Label text → Select "All Files" view -• [5] count → Read-only (no action) -``` - -### Visual Feedback - -**Default (Collapsed):** -``` -▶ 📁 All Files [5] -└─ Arrow right (▶) - Hover: slight background -``` - -**Expanded:** -``` -▼ 📂 All Files [5] - ▶ Documents - ▶ Photos -└─ Arrow down (▼, rotated 90°) - Open folder icon (📂) - Hover: slight background -``` - -## 🔄 State Flow - -``` -┌─────────────────────────────────────────────────────┐ -│ │ -│ DEFAULT STATE │ -│ ▶ All Files [collapsed] │ -│ │ -│ │ │ -│ ▼ │ -│ [User clicks ▶ button] │ -│ │ │ -│ ▼ │ -│ ▼ All Files [expanded] │ -│ ▶ Documents │ -│ ▶ Photos │ -│ ▶ Videos │ -│ │ │ -│ ▼ │ -│ [User clicks ▼ button] │ -│ │ │ -│ ▼ │ -│ ▶ All Files [collapsed] │ -│ │ -└─────────────────────────────────────────────────────┘ - -Auto-Expand Flow: -┌─────────────────────────────────────────────────────┐ -│ [User clicks "+ New"] │ -│ │ │ -│ ▼ │ -│ ▼ All Files [auto-expanded] │ -│ [Folder name input form] │ -│ │ │ -│ ▼ │ -│ [User creates folder] │ -│ │ │ -│ ▼ │ -│ ▼ All Files [remains expanded] │ -│ ▶ New Folder ✨ │ -│ ▶ Documents │ -│ ▶ Photos │ -└─────────────────────────────────────────────────────┘ -``` - -## 🧪 Testing - -### Test Cases: - -- [x] Default state is collapsed ✅ -- [x] Click ▶ expands folder tree ✅ -- [x] Click ▼ collapses folder tree ✅ -- [x] Icon rotates correctly (0° → 90°) ✅ -- [x] Folder count displays correctly ✅ -- [x] "New Folder" auto-expands "All Files" ✅ -- [x] Input form only visible when expanded ✅ -- [x] "All Files" selection still works ✅ -- [x] Individual folder expand/collapse works ✅ -- [x] Empty state (no folders) works ✅ -- [ ] User testing in browser (pending) - -### Edge Cases: - -1. **No Folders** - - ▶ button hidden ✅ - - "All Files" still clickable ✅ - - No folder count ✅ - -2. **Create Folder when Collapsed** - - Auto-expands "All Files" ✅ - - Input form visible ✅ - - User can create folder ✅ - -3. **Edit Folder when Collapsed** - - Auto-expands "All Files" ✅ - - Input form visible ✅ - - User can edit folder ✅ - -4. **Collapse while Editing** - - Input form hides ✅ - - Edit state preserved ✅ - - Re-expand shows form ✅ - -## 📊 Performance - -**State Management:** -- Single boolean state: `isAllFilesExpanded` -- No re-renders on folder operations -- Smooth transitions (CSS transform) - -**Render Optimization:** -- Conditional rendering: `isAllFilesExpanded && folders.map(...)` -- Only render folder tree when expanded -- Reduces initial render load - -**Performance Metrics:** -``` -Initial load (collapsed): ~50ms ✅ -Expand (render tree): ~100ms ✅ -Collapse (hide tree): ~10ms ✅ -Toggle animation: ~200ms ✅ -``` - -## 🎛️ Keyboard Shortcuts (Future Enhancement) - -Potential keyboard navigation: - -```typescript -useEffect(() => { - const handleKeyDown = (e: KeyboardEvent) => { - // Space: Toggle "All Files" expand/collapse - if (e.key === ' ' && isFolderTreeFocused) { - e.preventDefault(); - handleToggleAllFiles(); - } - - // Right Arrow: Expand "All Files" - if (e.key === 'ArrowRight' && !isAllFilesExpanded) { - setIsAllFilesExpanded(true); - } - - // Left Arrow: Collapse "All Files" - if (e.key === 'ArrowLeft' && isAllFilesExpanded) { - setIsAllFilesExpanded(false); - } - }; - - window.addEventListener('keydown', handleKeyDown); - return () => window.removeEventListener('keydown', handleKeyDown); -}, [isAllFilesExpanded, isFolderTreeFocused]); -``` - -## 📝 Code Changes Summary - -### Added State (1 line): -```typescript -const [isAllFilesExpanded, setIsAllFilesExpanded] = useState(false); // Mặc định collapsed -``` - -### Added Handler (3 lines): -```typescript -const handleToggleAllFiles = useCallback(() => { - setIsAllFilesExpanded(prev => !prev); -}, []); -``` - -### Updated Create Folder Handler (+4 lines): -```typescript -const handleCreateFolder = useCallback((parentId?: string) => { - setCreatingFolder({ parentId }); - setNewFolderName(''); - // Auto-expand "All Files" để hiển thị form - if (!isAllFilesExpanded) { - setIsAllFilesExpanded(true); - } -}, [isAllFilesExpanded]); -``` - -### Updated "All Files" UI (+26 lines): -- Added expand/collapse button -- Added folder count badge -- Restructured click handlers -- Improved accessibility - -**Total:** +34 lines (438 → 472) - -## ✅ Benefits - -### UX Improvements: - -1. **Cleaner Default UI** - - Less visual clutter - - Focus on "All Files" content - - Folder tree hidden by default - -2. **On-Demand Navigation** - - Expand when needed - - Collapse when done - - Quick toggle - -3. **Smart Auto-Expand** - - Auto-expand on "New Folder" - - User sees input form immediately - - No manual expand needed - -4. **Consistent Behavior** - - Same expand/collapse pattern as individual folders - - Familiar arrow icons (▶/▼) - - Smooth transitions - -### Developer Benefits: - -1. **Simple State Management** - - Single boolean state - - Easy to understand - - No complex logic - -2. **Performance Optimized** - - Conditional rendering - - Reduce initial load - - Smooth animations - -3. **Maintainable Code** - - Clear separation of concerns - - Reusable patterns - - Well-documented - -## 📅 Change Log - -- **2025-10-15** - "All Files" Collapse/Expand feature added - - Added `isAllFilesExpanded` state (default: `false`) - - Added `handleToggleAllFiles` handler - - Updated "All Files" row UI with expand/collapse button - - Added folder count badge to "All Files" - - Conditional rendering for folder tree - - Conditional rendering for create/edit form - - Auto-expand on "New Folder" click - - No linter errors - - No TypeScript errors - - Fully tested - -## 🔗 Related Features - -- **Collapse/Expand All** (lines 294-305) - - Works with "All Files" collapsed state - - Expands all individual folders (not "All Files" itself) - -- **Individual Folder Expand/Collapse** (lines 227-237) - - Independent of "All Files" state - - Nested folder navigation - -- **Folder Creation** (lines 240-247) - - Auto-expands "All Files" - - Shows input form - -## 🚀 Future Enhancements - -1. **Remember Collapse State** - ```typescript - const [isAllFilesExpanded, setIsAllFilesExpanded] = useState(() => { - const saved = localStorage.getItem('allFilesExpanded'); - return saved ? JSON.parse(saved) : false; - }); - - useEffect(() => { - localStorage.setItem('allFilesExpanded', JSON.stringify(isAllFilesExpanded)); - }, [isAllFilesExpanded]); - ``` - -2. **Keyboard Navigation** - - Space: Toggle - - Right Arrow: Expand - - Left Arrow: Collapse - -3. **Animation Options** - - Slide animation - - Fade animation - - Configurable duration - -4. **Accessibility** - - ARIA labels - - Screen reader support - - Keyboard focus management - - diff --git a/apps/client-example/docs/FOLDER_TREE_COLLAPSE_EXPAND.md b/apps/client-example/docs/FOLDER_TREE_COLLAPSE_EXPAND.md deleted file mode 100644 index 5c4e5877..00000000 --- a/apps/client-example/docs/FOLDER_TREE_COLLAPSE_EXPAND.md +++ /dev/null @@ -1,350 +0,0 @@ -# Folder Tree - Collapse/Expand All Feature - -## 📊 Tổng Quan - -**Date:** October 15, 2025 -**Status:** ✅ Completed -**Component:** `FolderTree.tsx` -**Lines:** 396 → 438 lines (+42 lines) - -## 🎯 Mục Đích - -Thêm tính năng **Collapse All / Expand All** để người dùng có thể thu gọn hoặc mở rộng tất cả folders cùng một lúc. - -## ✨ Features Đã Thêm - -### 1. **Collapse All / Expand All Button** - -**Location:** Header của FolderTree (bên phải, giữa folder count và New button) - -**Behavior:** -- **Click khi collapsed:** Mở rộng TẤT CẢ folders -- **Click khi expanded:** Thu gọn TẤT CẢ folders -- **Icon thay đổi:** ▼ (collapse) ↔ ▲ (expand) - -**Visual:** -``` -┌─────────────────────────────────────────────────┐ -│ Folders [5] [▼] [+ New] │ ← Collapsed -└─────────────────────────────────────────────────┘ - -Click [▼] → - -┌─────────────────────────────────────────────────┐ -│ Folders [5] [▲] [+ New] │ ← Expanded -└─────────────────────────────────────────────────┘ -``` - -### 2. **Folder Count Badge** - -**Visual:** -``` -┌─────────────────────────────────────────────────┐ -│ Folders [5] [▼] [+ New] │ -│ ▲ │ -│ └─ Số lượng folders (badge) │ -└─────────────────────────────────────────────────┘ -``` - -## 🔧 Technical Implementation - -### New State - -```typescript -const [isAllExpanded, setIsAllExpanded] = useState(false); -``` - -### Toggle All Handler - -```typescript -const handleToggleAll = useCallback(() => { - if (isAllExpanded) { - // Collapse all - clear all expanded folders - setExpandedFolders(new Set()); - setIsAllExpanded(false); - } else { - // Expand all - add all folder IDs - const allFolderIds = new Set(folders.map(f => f.id)); - setExpandedFolders(allFolderIds); - setIsAllExpanded(true); - } -}, [isAllExpanded, folders]); -``` - -### UI Components - -```tsx -{/* Collapse/Expand All Button */} -{folders.length > 0 && ( - -)} -``` - -## 📱 Visual Examples - -### State 1: All Collapsed (Default) - -``` -┌─────────────────────────────────────────────────┐ -│ Folders [5] [▲] [+ New] │ -├─────────────────────────────────────────────────┤ -│ 📁 All Files │ -│ ▶ 📁 Documents [3] │ -│ ▶ 📁 Photos [12] │ -│ ▶ 📁 Videos [8] │ -│ ▶ 📁 Projects [25] │ -│ ▶ 📁 Archive [156] │ -└─────────────────────────────────────────────────┘ - ▲ - └─ Collapsed (▶ arrow) -``` - -### State 2: All Expanded (Click [▲]) - -``` -┌─────────────────────────────────────────────────┐ -│ Folders [5] [▼] [+ New] │ -├─────────────────────────────────────────────────┤ -│ 📁 All Files │ -│ ▼ 📂 Documents [3] │ -│ ├─ ▶ 📁 Work [2] │ -│ └─ ▶ 📁 Personal [1] │ -│ ▼ 📂 Photos [12] │ -│ ├─ ▶ 📁 2024 [8] │ -│ └─ ▶ 📁 Vacation [4] │ -│ ▼ 📂 Videos [8] │ -│ └─ ▶ 📁 Tutorials [8] │ -│ ▼ 📂 Projects [25] │ -│ ├─ ▶ 📁 Client A [12] │ -│ ├─ ▶ 📁 Client B [8] │ -│ └─ ▶ 📁 Personal [5] │ -│ ▼ 📂 Archive [156] │ -│ ├─ ▶ 📁 2023 [89] │ -│ └─ ▶ 📁 2022 [67] │ -└─────────────────────────────────────────────────┘ - ▲ - └─ Expanded (▼ arrow, shows children) -``` - -## 🎯 Use Cases - -### 1. **Quick Overview** -Click **Collapse All** để xem toàn bộ root folders: -``` -▶ Documents -▶ Photos -▶ Videos -▶ Projects -``` - -### 2. **Deep Navigation** -Click **Expand All** để xem toàn bộ structure: -``` -▼ Documents - ├─ Work - │ ├─ Client A - │ └─ Client B - └─ Personal -``` - -### 3. **Find Nested Folder** -- Expand All -- Ctrl+F to search -- Navigate to deep folder - -## 🎨 UI/UX Details - -### Button States - -| State | Icon | Tooltip | Action | -|-------|------|---------|--------| -| **All Collapsed** | ▲ | "Expand All" | Expand tất cả | -| **All Expanded** | ▼ | "Collapse All" | Collapse tất cả | -| **Mixed** | ▲ | "Expand All" | Expand remaining | - -### Visual Feedback - -**Default (Collapsed):** -- Icon: ▲ (chevron up) -- Tooltip: "Expand All" -- Color: Gray - -**Expanded:** -- Icon: ▼ (chevron down) -- Tooltip: "Collapse All" -- Color: Gray - -**Hover:** -- Background: Light gray -- Text: Darker gray -- Smooth transition - -### Icon Design - -```tsx -// Collapsed state (▲) - - // Chevron pointing up - - -// Expanded state (▼) - - // Chevron pointing down - -``` - -## 🔄 State Management - -### Expansion State - -```typescript -// Track which folders are expanded -const [expandedFolders, setExpandedFolders] = useState>(new Set()); - -// Track if all folders are expanded -const [isAllExpanded, setIsAllExpanded] = useState(false); -``` - -### Toggle Logic - -**Expand All:** -1. Get all folder IDs -2. Add to `expandedFolders` Set -3. Set `isAllExpanded = true` - -**Collapse All:** -1. Clear `expandedFolders` Set -2. Set `isAllExpanded = false` - -## 🧪 Testing - -### Test Cases: - -- [x] Click Expand All → All folders expand -- [x] Click Collapse All → All folders collapse -- [x] Icon changes correctly -- [x] Tooltip shows correct text -- [x] Works with nested folders -- [x] Works with empty folder tree -- [x] Button only shows when folders > 0 -- [ ] User testing in browser (pending) - -### Edge Cases: - -1. **No Folders** - - Button hidden ✅ - -2. **Only Root Folders (no children)** - - Button visible but doesn't do much - - Still works correctly ✅ - -3. **Deep Nesting** - - Expand All shows all levels ✅ - - Collapse All hides all levels ✅ - -4. **Large Folder Tree (100+ folders)** - - Performance tested ✅ - - Smooth transitions ✅ - -## 📊 Performance - -**Before:** -- Manual expand: Click each folder individually (slow) -- Time for 20 folders: ~10 seconds - -**After:** -- One-click expand/collapse -- Time for 20 folders: ~0.1 second -- **100x faster!** ⚡ - -## 🎛️ Keyboard Shortcuts (Future) - -Potential enhancements: - -```typescript -useEffect(() => { - const handleKeyDown = (e: KeyboardEvent) => { - // Ctrl/Cmd + E: Expand All - if ((e.ctrlKey || e.metaKey) && e.key === 'e') { - e.preventDefault(); - handleToggleAll(); - } - }; - - window.addEventListener('keydown', handleKeyDown); - return () => window.removeEventListener('keydown', handleKeyDown); -}, [handleToggleAll]); -``` - -## 📝 Code Changes Summary - -### Added State (1 line): -```typescript -const [isAllExpanded, setIsAllExpanded] = useState(false); -``` - -### Added Handler (13 lines): -```typescript -const handleToggleAll = useCallback(() => { - if (isAllExpanded) { - setExpandedFolders(new Set()); - setIsAllExpanded(false); - } else { - const allFolderIds = new Set(folders.map(f => f.id)); - setExpandedFolders(allFolderIds); - setIsAllExpanded(true); - } -}, [isAllExpanded, folders]); -``` - -### Updated Header UI (28 lines): -- Added folder count badge -- Added collapse/expand all button -- Improved header layout - -**Total:** +42 lines - -## ✅ Benefits - -1. **UX Improvement** - - One-click expand/collapse - - Faster navigation - - Better overview - -2. **Productivity** - - Quick folder overview - - Easy deep navigation - - Time saved - -3. **Accessibility** - - Clear visual feedback - - Tooltips for clarity - - Keyboard ready - -## 📅 Change Log - -- **2025-10-15** - Collapse/Expand All feature added - - Added `isAllExpanded` state - - Added `handleToggleAll` handler - - Added toggle button in header - - Added folder count badge - - Updated header layout - - No linter errors - - No TypeScript errors - - Fully tested - - diff --git a/apps/client-example/docs/FOLDER_TREE_UPDATES_SUMMARY.md b/apps/client-example/docs/FOLDER_TREE_UPDATES_SUMMARY.md deleted file mode 100644 index fce193ba..00000000 --- a/apps/client-example/docs/FOLDER_TREE_UPDATES_SUMMARY.md +++ /dev/null @@ -1,341 +0,0 @@ -# Folder Tree - Updates Summary - -## 📊 Overview - -**Component:** `FolderTree.tsx` -**Total Changes:** 396 → 472 lines (+76 lines) -**Updates:** 2 major features -**Date:** October 15, 2025 - ---- - -## 🎯 Update 1: Collapse/Expand All Button - -**Lines Added:** +42 lines (396 → 438) - -### Features: -- ✅ Collapse All / Expand All button in header -- ✅ Folder count badge -- ✅ Smart toggle for all folders -- ✅ Visual feedback (▲/▼ icons) - -### State: -```typescript -const [isAllExpanded, setIsAllExpanded] = useState(false); -``` - -### Handler: -```typescript -const handleToggleAll = useCallback(() => { - if (isAllExpanded) { - setExpandedFolders(new Set()); - } else { - const allFolderIds = new Set(folders.map(f => f.id)); - setExpandedFolders(allFolderIds); - } -}, [isAllExpanded, folders]); -``` - -### UI: -``` -┌─────────────────────────────────────────────────┐ -│ Folders [5] [▼] [+ New] │ ← Header with toggle -├─────────────────────────────────────────────────┤ -│ 📁 All Files │ -│ ▼ 📂 Documents (expanded by toggle) │ -│ ├─ Work │ -│ └─ Personal │ -│ ▼ 📂 Photos (expanded by toggle) │ -│ └─ 2024 │ -└─────────────────────────────────────────────────┘ -``` - ---- - -## 🎯 Update 2: "All Files" Collapse/Expand - -**Lines Added:** +34 lines (438 → 472) - -### Features: -- ✅ "All Files" collapse/expand button -- ✅ **Default: COLLAPSED** (folder tree hidden) -- ✅ Folder count badge on "All Files" -- ✅ Auto-expand on "New Folder" click -- ✅ Conditional rendering for performance - -### State: -```typescript -const [isAllFilesExpanded, setIsAllFilesExpanded] = useState(false); // Default collapsed -``` - -### Handler: -```typescript -const handleToggleAllFiles = useCallback(() => { - setIsAllFilesExpanded(prev => !prev); -}, []); -``` - -### Auto-Expand: -```typescript -const handleCreateFolder = useCallback((parentId?: string) => { - setCreatingFolder({ parentId }); - if (!isAllFilesExpanded) { - setIsAllFilesExpanded(true); // Auto-expand! - } -}, [isAllFilesExpanded]); -``` - -### UI States: - -**Default (Collapsed):** -``` -┌─────────────────────────────────────────────────┐ -│ Folders [5] [▲] [+ New] │ -├─────────────────────────────────────────────────┤ -│ ▶ 📁 All Files [5] │ -│ │ -│ [Folder tree HIDDEN - Clean UI] │ -│ │ -└─────────────────────────────────────────────────┘ -``` - -**Expanded (Click ▶):** -``` -┌─────────────────────────────────────────────────┐ -│ Folders [5] [▲] [+ New] │ -├─────────────────────────────────────────────────┤ -│ ▼ 📂 All Files [5] │ -│ ▶ 📁 Documents [3] │ -│ ▶ 📁 Photos [12] │ -│ ▶ 📁 Videos [8] │ -└─────────────────────────────────────────────────┘ -``` - ---- - -## 🔄 Combined Features - -### Three Levels of Control: - -1. **"All Files" Toggle** (Update 2) - - Show/hide entire folder tree - - Default: Hidden (collapsed) - -2. **Individual Folder Toggle** (Existing) - - Expand/collapse specific folders - - Works independently - -3. **Collapse/Expand All Toggle** (Update 1) - - Toggle all folders at once - - Only affects individual folders, not "All Files" - -### Example Flow: - -``` -STEP 1: Default State -┌────────────────────────────────────┐ -│ ▶ 📁 All Files [5] │ ← Collapsed (tree hidden) -└────────────────────────────────────┘ - -STEP 2: Expand "All Files" (click ▶) -┌────────────────────────────────────┐ -│ ▼ 📂 All Files [5] │ ← Expanded -│ ▶ 📁 Documents [3] │ -│ ▶ 📁 Photos [12] │ -└────────────────────────────────────┘ - -STEP 3: Expand All (click header [▲]) -┌────────────────────────────────────┐ -│ ▼ 📂 All Files [5] │ -│ ▼ 📂 Documents [3] │ ← All expanded -│ ├─ Work │ -│ └─ Personal │ -│ ▼ 📂 Photos [12] │ -│ └─ 2024 │ -└────────────────────────────────────┘ - -STEP 4: Collapse All (click header [▼]) -┌────────────────────────────────────┐ -│ ▼ 📂 All Files [5] │ -│ ▶ 📁 Documents [3] │ ← All collapsed -│ ▶ 📁 Photos [12] │ -└────────────────────────────────────┘ - -STEP 5: Collapse "All Files" (click ▼) -┌────────────────────────────────────┐ -│ ▶ 📁 All Files [5] │ ← Back to default -└────────────────────────────────────┘ -``` - ---- - -## 📝 Code Statistics - -### Files Changed: -- `FolderTree.tsx`: +76 lines - -### State Variables Added: -1. `isAllExpanded: boolean` (Update 1) -2. `isAllFilesExpanded: boolean` (Update 2) - -### Handlers Added: -1. `handleToggleAll()` (Update 1) -2. `handleToggleAllFiles()` (Update 2) - -### UI Components Updated: -1. Header (folder count badge, collapse/expand all button) -2. "All Files" row (expand button, folder count, click handlers) -3. Folder hierarchy (conditional rendering) -4. Create/Edit form (conditional rendering) - ---- - -## ✅ Testing Checklist - -### Update 1: Collapse/Expand All -- [x] Button appears in header -- [x] Folder count badge displays -- [x] Click expands all folders -- [x] Click collapses all folders -- [x] Icon changes (▲ ↔ ▼) -- [x] No linter errors -- [x] No TypeScript errors - -### Update 2: "All Files" Collapse/Expand -- [x] Default state is collapsed -- [x] Expand button appears (▶) -- [x] Click expands folder tree -- [x] Click collapses folder tree -- [x] Icon rotates (0° → 90°) -- [x] Folder count displays -- [x] Auto-expand on "New Folder" -- [x] Conditional rendering works -- [x] No linter errors -- [x] No TypeScript errors - -### Integration Tests -- [ ] Both features work together -- [ ] No conflicts between toggles -- [ ] State management correct -- [ ] Performance optimized -- [ ] User testing in browser - ---- - -## 🎨 Visual Summary - -### Before Updates: -``` -┌─────────────────────────────────────┐ -│ Folders [+ New Folder]│ -├─────────────────────────────────────┤ -│ 📁 All Files │ -│ ▶ 📁 Documents │ ← Always visible -│ ▶ 📁 Photos │ -│ ▶ 📁 Videos │ -└─────────────────────────────────────┘ -``` - -### After Updates: -``` -┌─────────────────────────────────────┐ -│ Folders [5] [▲] [+ New] │ ← Count + Toggle All -├─────────────────────────────────────┤ -│ ▶ 📁 All Files [5] │ ← Collapsed + Count -│ │ -│ [Clean UI - tree hidden] │ -│ │ -└─────────────────────────────────────┘ - -Click ▶ on "All Files" + [▲] on header: - -┌─────────────────────────────────────┐ -│ Folders [5] [▼] [+ New] │ -├─────────────────────────────────────┤ -│ ▼ 📂 All Files [5] │ ← Expanded -│ ▼ 📂 Documents [3] │ ← All expanded -│ ├─ Work │ -│ └─ Personal │ -│ ▼ 📂 Photos [12] │ -│ └─ 2024 │ -└─────────────────────────────────────┘ -``` - ---- - -## 📚 Documentation - -### Files Created: -1. `FOLDER_TREE_COLLAPSE_EXPAND.md` (Update 1) -2. `FOLDER_TREE_ALL_FILES_COLLAPSE.md` (Update 2) -3. `FOLDER_TREE_UPDATES_SUMMARY.md` (This file) - -### Total Documentation: -- ~500 lines of detailed docs -- Visual examples -- Code snippets -- Use cases -- Testing guidelines - ---- - -## 🚀 Benefits - -### UX Improvements: -- ✅ Cleaner default UI (collapsed state) -- ✅ Less visual clutter -- ✅ One-click expand/collapse all -- ✅ On-demand folder navigation -- ✅ Smart auto-expand behavior -- ✅ Consistent interaction patterns - -### Performance: -- ✅ Conditional rendering (reduce initial load) -- ✅ Optimized re-renders -- ✅ Smooth CSS transitions -- ✅ Efficient state management - -### Developer Experience: -- ✅ Simple state management (2 booleans) -- ✅ Clear code structure -- ✅ Reusable patterns -- ✅ Well-documented -- ✅ No linter/TypeScript errors - ---- - -## 🎯 Next Steps - -### Recommended Testing: -1. Open browser: `http://localhost:3001/en/dashboard/storage` -2. Test default collapsed state -3. Test "All Files" expand/collapse -4. Test "Collapse/Expand All" button -5. Test "New Folder" auto-expand -6. Test with nested folders -7. Test with large folder trees (100+ folders) - -### Future Enhancements: -1. Remember collapse state (localStorage) -2. Keyboard shortcuts (Space, Arrow keys) -3. Animation options (slide, fade) -4. Accessibility improvements (ARIA labels) -5. Mobile optimization - ---- - -## ✅ Status - -**All Features:** ✅ COMPLETE -**Linter Errors:** ✅ NONE -**TypeScript Errors:** ✅ NONE -**Documentation:** ✅ COMPLETE -**Ready for Testing:** ✅ YES - ---- - -**Last Updated:** October 15, 2025 -**Component Version:** 2.0 -**Total Lines:** 472 - - diff --git a/apps/client-example/docs/GOOGLE_OAUTH_FRONTEND_INTEGRATION.md b/apps/client-example/docs/GOOGLE_OAUTH_FRONTEND_INTEGRATION.md deleted file mode 100644 index 38e4bc8f..00000000 --- a/apps/client-example/docs/GOOGLE_OAUTH_FRONTEND_INTEGRATION.md +++ /dev/null @@ -1,183 +0,0 @@ -# Google OAuth Frontend Integration - -## ✅ **Implementation hoàn thành** - -### 🎯 **Components đã tạo** - -#### 1. **GoogleSignInButton Component** (`/components/auth/GoogleSignInButton.tsx`) -- Beautiful Google button với official icon -- Dark mode support -- Loading states -- "Or continue with" divider -- Modes: `signin` và `signup` - -#### 2. **Google Callback Page** (`/app/[locale]/auth/google/callback/page.tsx`) -- Xử lý OAuth redirect từ Google -- State validation cho CSRF protection -- Token exchange với backend -- Loading animation -- Error handling với auto-redirect - -#### 3. **Auth Service Updates** (`/lib/auth.service.ts`) -- `getGoogleAuthUrl()`: Lấy OAuth URL -- `handleGoogleCallback()`: Exchange code for tokens -- `unlinkGoogleAccount()`: Remove Google link - -### 📋 **API Endpoints sử dụng** - -| Endpoint | Method | Port | Description | -|----------|--------|------|-------------| -| `/api/auth/google` | GET | 7001 | Lấy Google OAuth URL | -| `/api/auth/google/callback` | POST | 7001 | Exchange code for tokens | -| `/api/auth/google/unlink` | DELETE | 7001 | Unlink Google account | - -### 🔄 **OAuth Flow** - -```mermaid -sequenceDiagram - participant User - participant Frontend - participant AuthService - participant Google - - User->>Frontend: Click "Sign in with Google" - Frontend->>AuthService: GET /api/auth/google - AuthService-->>Frontend: Return OAuth URL + state - Frontend->>Google: Redirect to OAuth URL - User->>Google: Authorize access - Google->>Frontend: Redirect to /auth/google/callback - Frontend->>AuthService: POST /api/auth/google/callback - AuthService-->>Frontend: Return user + tokens - Frontend->>User: Redirect to Dashboard -``` - -### 🎨 **UI Features** - -```typescript -// Login Form Integration - - -// Register Form Integration - -``` - -### 🌐 **Translations** - -```json -// English (en.json) -{ - "Auth": { - "signInWithGoogle": "Sign in with Google", - "signUpWithGoogle": "Sign up with Google", - "orContinueWith": "Or continue with", - "googleSignInError": "Google sign-in failed. Please try again.", - "signingIn": "Signing in...", - "processingGoogleSignIn": "Processing Google Sign-In...", - "pleaseWait": "Please wait while we complete your authentication.", - "authenticationFailed": "Authentication Failed", - "redirectingToLogin": "Redirecting to login page...", - "signUpSuccess": "Account created successfully!", - "signInSuccess": "Signed in successfully!" - } -} - -// Vietnamese (vi.json) -{ - "Auth": { - "signInWithGoogle": "Đăng nhập với Google", - "signUpWithGoogle": "Đăng ký với Google", - "orContinueWith": "Hoặc tiếp tục với", - "googleSignInError": "Đăng nhập Google thất bại. Vui lòng thử lại.", - "signingIn": "Đang đăng nhập...", - "processingGoogleSignIn": "Đang xử lý đăng nhập Google...", - "pleaseWait": "Vui lòng đợi trong khi chúng tôi hoàn tất xác thực.", - "authenticationFailed": "Xác thực thất bại", - "redirectingToLogin": "Chuyển hướng đến trang đăng nhập...", - "signUpSuccess": "Tạo tài khoản thành công!", - "signInSuccess": "Đăng nhập thành công!" - } -} -``` - -### 🔒 **Security Features** - -1. **CSRF Protection**: State parameter validation -2. **Token Management**: Secure JWT storage -3. **Session Storage**: OAuth state temporary storage -4. **Error Recovery**: Graceful error handling - -### 🛠️ **Environment Variables** - -```env -# Client (.env) -NEXT_PUBLIC_AUTH_SERVICE_URL=http://localhost:7001 - -# Auth Service (.env) -GOOGLE_CLIENT_ID=52025673893-ciciobong7jsmjv6gif65ft126j059n9.apps.googleusercontent.com -GOOGLE_CLIENT_SECRET=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 -GOOGLE_REDIRECT_URI=http://localhost:7001/api/auth/google/callback -GOOGLE_SCOPE=profile email -``` - -### 🧪 **Testing Steps** - -1. **Start services:** -```bash -# Auth Service -npm run dev:auth - -# NextJS Client -cd client && npm run dev -``` - -2. **Test Login:** -- Navigate to http://localhost:3001/en/auth/login -- Click "Sign in with Google" -- Complete Google authentication -- Verify redirect to dashboard - -3. **Test Register:** -- Navigate to http://localhost:3001/en/auth/register -- Click "Sign up with Google" -- Complete Google authentication -- Verify account creation - -### ⚠️ **Google Cloud Console Setup Required** - -1. **Authorized redirect URIs:** - - `http://localhost:7001/api/auth/google/callback` - -2. **Authorized JavaScript origins:** - - `http://localhost:7001` - - `http://localhost:3001` - -3. **Enable APIs:** - - Google+ API - - People API - -### 📊 **Implementation Status** - -| Feature | Status | Notes | -|---------|--------|-------| -| Google Sign-In Button | ✅ Complete | Beautiful UI with dark mode | -| OAuth Flow | ✅ Complete | Full authentication cycle | -| Account Linking | ✅ Complete | Auto-link existing emails | -| Error Handling | ✅ Complete | Graceful error recovery | -| Translations | ✅ Complete | English & Vietnamese | -| Security | ✅ Complete | CSRF protection | -| Linter Errors | ✅ Fixed | All TypeScript errors resolved | - -### 🎉 **Result** - -Google OAuth integration hoàn toàn functional với: -- **Beautiful UI** matching design system -- **Seamless UX** với loading states -- **Secure implementation** với best practices -- **Full i18n support** cho English và Vietnamese -- **TypeScript compliant** không có linter errors diff --git a/apps/client-example/docs/HYDRATION_FIX.md b/apps/client-example/docs/HYDRATION_FIX.md deleted file mode 100644 index f67c6ff5..00000000 --- a/apps/client-example/docs/HYDRATION_FIX.md +++ /dev/null @@ -1,71 +0,0 @@ -# 🔧 Hydration Warning Fix - -## ❌ Warning: -``` -Warning: Extra attributes from the server: class -at html/body tags -``` - -## ✅ Nguyên nhân: -- Server render HTML với một class -- Client (localStorage) có thể có theme khác -- → Class mismatch → Hydration warning - -## 🛠️ Giải pháp: - -### 1. **Blocking Script trong ``** -```typescript - -