@nextsparkjs/core 0.1.0-beta.83 → 0.1.0-beta.85
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/styles/classes.json +1 -1
- package/dist/templates/app/(auth)/forgot-password/page.tsx +216 -0
- package/dist/templates/app/(auth)/layout.tsx +51 -0
- package/dist/templates/app/(auth)/login/page.tsx +21 -0
- package/dist/templates/app/(auth)/reset-password/page.tsx +212 -0
- package/dist/templates/app/(auth)/signup/page.tsx +21 -0
- package/dist/templates/app/(auth)/verify-email/page.tsx +190 -0
- package/dist/templates/app/(public)/[...slug]/page.tsx +378 -0
- package/dist/templates/app/(public)/docs/[section]/[page]/page.tsx +90 -0
- package/dist/templates/app/(public)/docs/layout.tsx +25 -0
- package/dist/templates/app/(public)/docs/page.tsx +81 -0
- package/dist/templates/app/(public)/layout.tsx +41 -0
- package/dist/templates/app/(public)/page.tsx +19 -0
- package/dist/templates/app/403/page.tsx +89 -0
- package/dist/templates/app/api/auth/[...all]/route.ts +78 -0
- package/dist/templates/app/api/cron/billing/lifecycle/route.ts +98 -0
- package/dist/templates/app/api/csp-report/route.ts +175 -0
- package/dist/templates/app/api/devtools/config/entities/route.ts +108 -0
- package/dist/templates/app/api/devtools/config/theme/route.ts +66 -0
- package/dist/templates/app/api/devtools/tests/[...path]/route.ts +130 -0
- package/dist/templates/app/api/devtools/tests/route.ts +134 -0
- package/dist/templates/app/api/health/route.ts +29 -0
- package/dist/templates/app/api/internal/user-metadata/route.ts +36 -0
- package/dist/templates/app/api/superadmin/subscriptions/route.ts +310 -0
- package/dist/templates/app/api/superadmin/teams/[teamId]/route.ts +286 -0
- package/dist/templates/app/api/superadmin/teams/route.ts +188 -0
- package/dist/templates/app/api/superadmin/users/[userId]/route.ts +540 -0
- package/dist/templates/app/api/superadmin/users/route.ts +323 -0
- package/dist/templates/app/api/user/delete-account/route.ts +55 -0
- package/dist/templates/app/api/user/plan-flags/route.ts +283 -0
- package/dist/templates/app/api/user/profile/route.ts +133 -0
- package/dist/templates/app/api/v1/[entity]/[id]/child/[childType]/[childId]/route.ts +210 -0
- package/dist/templates/app/api/v1/[entity]/[id]/child/[childType]/route.ts +331 -0
- package/dist/templates/app/api/v1/[entity]/[id]/route.ts +35 -0
- package/dist/templates/app/api/v1/[entity]/docs.md +369 -0
- package/dist/templates/app/api/v1/[entity]/presets.ts +194 -0
- package/dist/templates/app/api/v1/[entity]/route.ts +31 -0
- package/dist/templates/app/api/v1/api-keys/[id]/route.ts +303 -0
- package/dist/templates/app/api/v1/api-keys/docs.md +101 -0
- package/dist/templates/app/api/v1/api-keys/presets.ts +31 -0
- package/dist/templates/app/api/v1/api-keys/route.ts +250 -0
- package/dist/templates/app/api/v1/auth/docs.md +184 -0
- package/dist/templates/app/api/v1/auth/presets.ts +44 -0
- package/dist/templates/app/api/v1/auth/signup-with-invite/route.ts +227 -0
- package/dist/templates/app/api/v1/billing/cancel/route.ts +206 -0
- package/dist/templates/app/api/v1/billing/change-plan/route.ts +97 -0
- package/dist/templates/app/api/v1/billing/check-action/route.ts +81 -0
- package/dist/templates/app/api/v1/billing/checkout/route.ts +124 -0
- package/dist/templates/app/api/v1/billing/docs.md +209 -0
- package/dist/templates/app/api/v1/billing/plans/route.ts +85 -0
- package/dist/templates/app/api/v1/billing/portal/route.ts +90 -0
- package/dist/templates/app/api/v1/billing/presets.ts +121 -0
- package/dist/templates/app/api/v1/billing/webhooks/stripe/route.ts +428 -0
- package/dist/templates/app/api/v1/blocks/[slug]/route.ts +29 -0
- package/dist/templates/app/api/v1/blocks/docs.md +173 -0
- package/dist/templates/app/api/v1/blocks/presets.ts +121 -0
- package/dist/templates/app/api/v1/blocks/route.ts +45 -0
- package/dist/templates/app/api/v1/blocks/validate/route.ts +45 -0
- package/dist/templates/app/api/v1/cron/docs.md +116 -0
- package/dist/templates/app/api/v1/cron/presets.ts +26 -0
- package/dist/templates/app/api/v1/cron/process/route.ts +108 -0
- package/dist/templates/app/api/v1/devtools/blocks/route.ts +82 -0
- package/dist/templates/app/api/v1/devtools/docs/route.ts +150 -0
- package/dist/templates/app/api/v1/devtools/docs.md +204 -0
- package/dist/templates/app/api/v1/devtools/features/route.ts +61 -0
- package/dist/templates/app/api/v1/devtools/flows/route.ts +61 -0
- package/dist/templates/app/api/v1/devtools/presets.ts +113 -0
- package/dist/templates/app/api/v1/devtools/scheduled-actions/route.ts +120 -0
- package/dist/templates/app/api/v1/devtools/testing/route.ts +82 -0
- package/dist/templates/app/api/v1/media/docs.md +117 -0
- package/dist/templates/app/api/v1/media/presets.ts +24 -0
- package/dist/templates/app/api/v1/media/upload/route.ts +150 -0
- package/dist/templates/app/api/v1/patterns/[id]/usages/route.ts +116 -0
- package/dist/templates/app/api/v1/plugin/[...path]/route.ts +373 -0
- package/dist/templates/app/api/v1/plugin/docs.md +79 -0
- package/dist/templates/app/api/v1/plugin/presets.ts +21 -0
- package/dist/templates/app/api/v1/plugin/route.ts +96 -0
- package/dist/templates/app/api/v1/post-categories/[id]/route.ts +255 -0
- package/dist/templates/app/api/v1/post-categories/docs.md +134 -0
- package/dist/templates/app/api/v1/post-categories/presets.ts +78 -0
- package/dist/templates/app/api/v1/post-categories/route.ts +119 -0
- package/dist/templates/app/api/v1/team-invitations/[token]/accept/route.ts +179 -0
- package/dist/templates/app/api/v1/team-invitations/[token]/decline/route.ts +120 -0
- package/dist/templates/app/api/v1/team-invitations/[token]/route.ts +89 -0
- package/dist/templates/app/api/v1/team-invitations/docs.md +88 -0
- package/dist/templates/app/api/v1/team-invitations/presets.ts +43 -0
- package/dist/templates/app/api/v1/team-invitations/route.ts +114 -0
- package/dist/templates/app/api/v1/teams/[teamId]/invitations/route.ts +171 -0
- package/dist/templates/app/api/v1/teams/[teamId]/invoices/[invoiceNumber]/route.ts +105 -0
- package/dist/templates/app/api/v1/teams/[teamId]/invoices/route.ts +125 -0
- package/dist/templates/app/api/v1/teams/[teamId]/members/[memberId]/route.ts +263 -0
- package/dist/templates/app/api/v1/teams/[teamId]/members/route.ts +358 -0
- package/dist/templates/app/api/v1/teams/[teamId]/route.ts +322 -0
- package/dist/templates/app/api/v1/teams/[teamId]/subscription/route.ts +50 -0
- package/dist/templates/app/api/v1/teams/[teamId]/usage/[limitSlug]/route.ts +91 -0
- package/dist/templates/app/api/v1/teams/docs.md +320 -0
- package/dist/templates/app/api/v1/teams/presets.ts +178 -0
- package/dist/templates/app/api/v1/teams/route.ts +293 -0
- package/dist/templates/app/api/v1/teams/switch/route.ts +88 -0
- package/dist/templates/app/api/v1/theme/[...path]/route.ts +361 -0
- package/dist/templates/app/api/v1/theme/docs.md +74 -0
- package/dist/templates/app/api/v1/theme/presets.ts +21 -0
- package/dist/templates/app/api/v1/theme/route.ts +96 -0
- package/dist/templates/app/api/v1/users/[id]/meta/[key]/route.ts +363 -0
- package/dist/templates/app/api/v1/users/[id]/route.ts +302 -0
- package/dist/templates/app/api/v1/users/docs.md +93 -0
- package/dist/templates/app/api/v1/users/presets.ts +59 -0
- package/dist/templates/app/api/v1/users/route.ts +197 -0
- package/dist/templates/app/dashboard/(main)/[entity]/[id]/edit/page.tsx +117 -0
- package/dist/templates/app/dashboard/(main)/[entity]/[id]/page.tsx +103 -0
- package/dist/templates/app/dashboard/(main)/[entity]/create/page.tsx +95 -0
- package/dist/templates/app/dashboard/(main)/[entity]/error.tsx +51 -0
- package/dist/templates/app/dashboard/(main)/[entity]/layout.tsx +113 -0
- package/dist/templates/app/dashboard/(main)/[entity]/loading.tsx +61 -0
- package/dist/templates/app/dashboard/(main)/[entity]/page.tsx +90 -0
- package/dist/templates/app/dashboard/(main)/layout.tsx +98 -0
- package/dist/templates/app/dashboard/(main)/loading.tsx +5 -0
- package/dist/templates/app/dashboard/(main)/page.tsx +201 -0
- package/dist/templates/app/dashboard/(main)/patterns/[id]/edit/page.tsx +114 -0
- package/dist/templates/app/dashboard/(main)/patterns/[id]/page.tsx +20 -0
- package/dist/templates/app/dashboard/(main)/patterns/[id]/reports/page.tsx +171 -0
- package/dist/templates/app/dashboard/(main)/patterns/create/page.tsx +86 -0
- package/dist/templates/app/dashboard/(main)/patterns/page.tsx +444 -0
- package/dist/templates/app/dashboard/features/analytics/page.tsx +35 -0
- package/dist/templates/app/dashboard/features/automation/page.tsx +35 -0
- package/dist/templates/app/dashboard/features/layout.tsx +13 -0
- package/dist/templates/app/dashboard/features/loading.tsx +5 -0
- package/dist/templates/app/dashboard/features/webhooks/page.tsx +35 -0
- package/dist/templates/app/dashboard/layout.tsx +86 -0
- package/dist/templates/app/dashboard/permission-denied/page.tsx +29 -0
- package/dist/templates/app/dashboard/settings/api-keys/loading.tsx +5 -0
- package/dist/templates/app/dashboard/settings/api-keys/page.tsx +513 -0
- package/dist/templates/app/dashboard/settings/billing/loading.tsx +5 -0
- package/dist/templates/app/dashboard/settings/billing/page.tsx +284 -0
- package/dist/templates/app/dashboard/settings/invoices/[invoiceNumber]/page.tsx +222 -0
- package/dist/templates/app/dashboard/settings/invoices/loading.tsx +5 -0
- package/dist/templates/app/dashboard/settings/invoices/page.tsx +82 -0
- package/dist/templates/app/dashboard/settings/layout.tsx +151 -0
- package/dist/templates/app/dashboard/settings/loading.tsx +5 -0
- package/dist/templates/app/dashboard/settings/notifications/loading.tsx +5 -0
- package/dist/templates/app/dashboard/settings/notifications/page.tsx +462 -0
- package/dist/templates/app/dashboard/settings/page.tsx +92 -0
- package/dist/templates/app/dashboard/settings/password/loading.tsx +5 -0
- package/dist/templates/app/dashboard/settings/password/page.tsx +306 -0
- package/dist/templates/app/dashboard/settings/plans/loading.tsx +5 -0
- package/dist/templates/app/dashboard/settings/plans/page.tsx +40 -0
- package/dist/templates/app/dashboard/settings/profile/loading.tsx +5 -0
- package/dist/templates/app/dashboard/settings/profile/page.tsx +686 -0
- package/dist/templates/app/dashboard/settings/security/loading.tsx +5 -0
- package/dist/templates/app/dashboard/settings/security/page.tsx +505 -0
- package/dist/templates/app/dashboard/settings/teams/loading.tsx +5 -0
- package/dist/templates/app/dashboard/settings/teams/page.tsx +272 -0
- package/dist/templates/app/dashboard/settings/teams/permissions/page.tsx +92 -0
- package/dist/templates/app/devtools/blocks/[slug]/page.tsx +39 -0
- package/dist/templates/app/devtools/blocks/page.tsx +31 -0
- package/dist/templates/app/devtools/config/page.tsx +31 -0
- package/dist/templates/app/devtools/features/page.tsx +31 -0
- package/dist/templates/app/devtools/flows/page.tsx +31 -0
- package/dist/templates/app/devtools/layout.tsx +58 -0
- package/dist/templates/app/devtools/page.tsx +121 -0
- package/dist/templates/app/devtools/scheduled-actions/page.tsx +157 -0
- package/dist/templates/app/devtools/style/page.tsx +330 -0
- package/dist/templates/app/devtools/tags/page.tsx +31 -0
- package/dist/templates/app/devtools/tests/[[...path]]/page.tsx +47 -0
- package/dist/templates/app/favicon.ico +0 -0
- package/dist/templates/app/globals.css +12 -0
- package/dist/templates/app/layout.tsx +96 -0
- package/dist/templates/app/public/page.tsx +30 -0
- package/dist/templates/app/superadmin/docs/[section]/[page]/page.tsx +92 -0
- package/dist/templates/app/superadmin/docs/page.tsx +75 -0
- package/dist/templates/app/superadmin/layout.tsx +67 -0
- package/dist/templates/app/superadmin/page.tsx +149 -0
- package/dist/templates/app/superadmin/subscriptions/page.tsx +655 -0
- package/dist/templates/app/superadmin/team-roles/page.tsx +493 -0
- package/dist/templates/app/superadmin/teams/[teamId]/page.tsx +687 -0
- package/dist/templates/app/superadmin/teams/page.tsx +302 -0
- package/dist/templates/app/superadmin/users/[userId]/page.tsx +548 -0
- package/dist/templates/app/superadmin/users/page.tsx +528 -0
- package/package.json +15 -15
- package/scripts/build/docs-registry.mjs +0 -0
- package/scripts/create-theme.mjs +0 -0
- package/scripts/deploy/release-version.mjs +0 -0
- package/scripts/deploy/vercel-deploy.mjs +0 -0
- package/scripts/dev/watch-plugins.mjs +0 -0
- package/scripts/maintenance/update-core.mjs +0 -0
- package/scripts/setup/npm-postinstall.mjs +0 -0
- package/scripts/setup/setup-claude.mjs +0 -0
- package/scripts/validation/check-imports.sh +0 -0
- package/templates/app/(auth)/forgot-password/page.tsx +216 -0
- package/templates/app/(auth)/layout.tsx +51 -0
- package/templates/app/(auth)/login/page.tsx +21 -0
- package/templates/app/(auth)/reset-password/page.tsx +212 -0
- package/templates/app/(auth)/signup/page.tsx +21 -0
- package/templates/app/(auth)/verify-email/page.tsx +190 -0
- package/templates/app/(public)/[...slug]/page.tsx +378 -0
- package/templates/app/(public)/docs/[section]/[page]/page.tsx +90 -0
- package/templates/app/(public)/docs/layout.tsx +25 -0
- package/templates/app/(public)/docs/page.tsx +81 -0
- package/templates/app/(public)/layout.tsx +41 -0
- package/templates/app/(public)/page.tsx +19 -0
- package/templates/app/403/page.tsx +89 -0
- package/templates/app/api/auth/[...all]/route.ts +78 -0
- package/templates/app/api/cron/billing/lifecycle/route.ts +98 -0
- package/templates/app/api/csp-report/route.ts +175 -0
- package/templates/app/api/devtools/config/entities/route.ts +108 -0
- package/templates/app/api/devtools/config/theme/route.ts +66 -0
- package/templates/app/api/devtools/tests/[...path]/route.ts +130 -0
- package/templates/app/api/devtools/tests/route.ts +134 -0
- package/templates/app/api/health/route.ts +29 -0
- package/templates/app/api/internal/user-metadata/route.ts +36 -0
- package/templates/app/api/superadmin/subscriptions/route.ts +310 -0
- package/templates/app/api/superadmin/teams/[teamId]/route.ts +286 -0
- package/templates/app/api/superadmin/teams/route.ts +188 -0
- package/templates/app/api/superadmin/users/[userId]/route.ts +540 -0
- package/templates/app/api/superadmin/users/route.ts +323 -0
- package/templates/app/api/user/delete-account/route.ts +55 -0
- package/templates/app/api/user/plan-flags/route.ts +283 -0
- package/templates/app/api/user/profile/route.ts +133 -0
- package/templates/app/api/v1/[entity]/[id]/child/[childType]/[childId]/route.ts +210 -0
- package/templates/app/api/v1/[entity]/[id]/child/[childType]/route.ts +331 -0
- package/templates/app/api/v1/[entity]/[id]/route.ts +35 -0
- package/templates/app/api/v1/[entity]/docs.md +369 -0
- package/templates/app/api/v1/[entity]/presets.ts +194 -0
- package/templates/app/api/v1/[entity]/route.ts +31 -0
- package/templates/app/api/v1/api-keys/[id]/route.ts +303 -0
- package/templates/app/api/v1/api-keys/docs.md +101 -0
- package/templates/app/api/v1/api-keys/presets.ts +31 -0
- package/templates/app/api/v1/api-keys/route.ts +250 -0
- package/templates/app/api/v1/auth/docs.md +184 -0
- package/templates/app/api/v1/auth/presets.ts +44 -0
- package/templates/app/api/v1/auth/signup-with-invite/route.ts +227 -0
- package/templates/app/api/v1/billing/cancel/route.ts +206 -0
- package/templates/app/api/v1/billing/change-plan/route.ts +97 -0
- package/templates/app/api/v1/billing/check-action/route.ts +81 -0
- package/templates/app/api/v1/billing/checkout/route.ts +124 -0
- package/templates/app/api/v1/billing/docs.md +209 -0
- package/templates/app/api/v1/billing/plans/route.ts +85 -0
- package/templates/app/api/v1/billing/portal/route.ts +90 -0
- package/templates/app/api/v1/billing/presets.ts +121 -0
- package/templates/app/api/v1/billing/webhooks/stripe/route.ts +428 -0
- package/templates/app/api/v1/blocks/[slug]/route.ts +29 -0
- package/templates/app/api/v1/blocks/docs.md +173 -0
- package/templates/app/api/v1/blocks/presets.ts +121 -0
- package/templates/app/api/v1/blocks/route.ts +45 -0
- package/templates/app/api/v1/blocks/validate/route.ts +45 -0
- package/templates/app/api/v1/cron/docs.md +116 -0
- package/templates/app/api/v1/cron/presets.ts +26 -0
- package/templates/app/api/v1/cron/process/route.ts +108 -0
- package/templates/app/api/v1/devtools/blocks/route.ts +82 -0
- package/templates/app/api/v1/devtools/docs/route.ts +150 -0
- package/templates/app/api/v1/devtools/docs.md +204 -0
- package/templates/app/api/v1/devtools/features/route.ts +61 -0
- package/templates/app/api/v1/devtools/flows/route.ts +61 -0
- package/templates/app/api/v1/devtools/presets.ts +113 -0
- package/templates/app/api/v1/devtools/scheduled-actions/route.ts +120 -0
- package/templates/app/api/v1/devtools/testing/route.ts +82 -0
- package/templates/app/api/v1/media/docs.md +117 -0
- package/templates/app/api/v1/media/presets.ts +24 -0
- package/templates/app/api/v1/media/upload/route.ts +150 -0
- package/templates/app/api/v1/patterns/[id]/usages/route.ts +116 -0
- package/templates/app/api/v1/plugin/[...path]/route.ts +373 -0
- package/templates/app/api/v1/plugin/docs.md +79 -0
- package/templates/app/api/v1/plugin/presets.ts +21 -0
- package/templates/app/api/v1/plugin/route.ts +96 -0
- package/templates/app/api/v1/post-categories/[id]/route.ts +255 -0
- package/templates/app/api/v1/post-categories/docs.md +134 -0
- package/templates/app/api/v1/post-categories/presets.ts +78 -0
- package/templates/app/api/v1/post-categories/route.ts +119 -0
- package/templates/app/api/v1/team-invitations/[token]/accept/route.ts +179 -0
- package/templates/app/api/v1/team-invitations/[token]/decline/route.ts +120 -0
- package/templates/app/api/v1/team-invitations/[token]/route.ts +89 -0
- package/templates/app/api/v1/team-invitations/docs.md +88 -0
- package/templates/app/api/v1/team-invitations/presets.ts +43 -0
- package/templates/app/api/v1/team-invitations/route.ts +114 -0
- package/templates/app/api/v1/teams/[teamId]/invitations/route.ts +171 -0
- package/templates/app/api/v1/teams/[teamId]/invoices/[invoiceNumber]/route.ts +105 -0
- package/templates/app/api/v1/teams/[teamId]/invoices/route.ts +125 -0
- package/templates/app/api/v1/teams/[teamId]/members/[memberId]/route.ts +263 -0
- package/templates/app/api/v1/teams/[teamId]/members/route.ts +358 -0
- package/templates/app/api/v1/teams/[teamId]/route.ts +322 -0
- package/templates/app/api/v1/teams/[teamId]/subscription/route.ts +50 -0
- package/templates/app/api/v1/teams/[teamId]/usage/[limitSlug]/route.ts +91 -0
- package/templates/app/api/v1/teams/docs.md +320 -0
- package/templates/app/api/v1/teams/presets.ts +178 -0
- package/templates/app/api/v1/teams/route.ts +293 -0
- package/templates/app/api/v1/teams/switch/route.ts +88 -0
- package/templates/app/api/v1/theme/[...path]/route.ts +361 -0
- package/templates/app/api/v1/theme/docs.md +74 -0
- package/templates/app/api/v1/theme/presets.ts +21 -0
- package/templates/app/api/v1/theme/route.ts +96 -0
- package/templates/app/api/v1/users/[id]/meta/[key]/route.ts +363 -0
- package/templates/app/api/v1/users/[id]/route.ts +302 -0
- package/templates/app/api/v1/users/docs.md +93 -0
- package/templates/app/api/v1/users/presets.ts +59 -0
- package/templates/app/api/v1/users/route.ts +197 -0
- package/templates/app/dashboard/(main)/[entity]/[id]/edit/page.tsx +117 -0
- package/templates/app/dashboard/(main)/[entity]/[id]/page.tsx +103 -0
- package/templates/app/dashboard/(main)/[entity]/create/page.tsx +95 -0
- package/templates/app/dashboard/(main)/[entity]/error.tsx +51 -0
- package/templates/app/dashboard/(main)/[entity]/layout.tsx +113 -0
- package/templates/app/dashboard/(main)/[entity]/loading.tsx +61 -0
- package/templates/app/dashboard/(main)/[entity]/page.tsx +90 -0
- package/templates/app/dashboard/(main)/layout.tsx +98 -0
- package/templates/app/dashboard/(main)/loading.tsx +5 -0
- package/templates/app/dashboard/(main)/page.tsx +201 -0
- package/templates/app/dashboard/(main)/patterns/[id]/edit/page.tsx +114 -0
- package/templates/app/dashboard/(main)/patterns/[id]/page.tsx +20 -0
- package/templates/app/dashboard/(main)/patterns/[id]/reports/page.tsx +171 -0
- package/templates/app/dashboard/(main)/patterns/create/page.tsx +86 -0
- package/templates/app/dashboard/(main)/patterns/page.tsx +444 -0
- package/templates/app/dashboard/features/analytics/page.tsx +35 -0
- package/templates/app/dashboard/features/automation/page.tsx +35 -0
- package/templates/app/dashboard/features/layout.tsx +13 -0
- package/templates/app/dashboard/features/loading.tsx +5 -0
- package/templates/app/dashboard/features/webhooks/page.tsx +35 -0
- package/templates/app/dashboard/layout.tsx +86 -0
- package/templates/app/dashboard/permission-denied/page.tsx +29 -0
- package/templates/app/dashboard/settings/api-keys/loading.tsx +5 -0
- package/templates/app/dashboard/settings/api-keys/page.tsx +513 -0
- package/templates/app/dashboard/settings/billing/loading.tsx +5 -0
- package/templates/app/dashboard/settings/billing/page.tsx +284 -0
- package/templates/app/dashboard/settings/invoices/[invoiceNumber]/page.tsx +222 -0
- package/templates/app/dashboard/settings/invoices/loading.tsx +5 -0
- package/templates/app/dashboard/settings/invoices/page.tsx +82 -0
- package/templates/app/dashboard/settings/layout.tsx +151 -0
- package/templates/app/dashboard/settings/loading.tsx +5 -0
- package/templates/app/dashboard/settings/notifications/loading.tsx +5 -0
- package/templates/app/dashboard/settings/notifications/page.tsx +462 -0
- package/templates/app/dashboard/settings/page.tsx +92 -0
- package/templates/app/dashboard/settings/password/loading.tsx +5 -0
- package/templates/app/dashboard/settings/password/page.tsx +306 -0
- package/templates/app/dashboard/settings/plans/loading.tsx +5 -0
- package/templates/app/dashboard/settings/plans/page.tsx +40 -0
- package/templates/app/dashboard/settings/profile/loading.tsx +5 -0
- package/templates/app/dashboard/settings/profile/page.tsx +686 -0
- package/templates/app/dashboard/settings/security/loading.tsx +5 -0
- package/templates/app/dashboard/settings/security/page.tsx +505 -0
- package/templates/app/dashboard/settings/teams/loading.tsx +5 -0
- package/templates/app/dashboard/settings/teams/page.tsx +272 -0
- package/templates/app/dashboard/settings/teams/permissions/page.tsx +92 -0
- package/templates/app/devtools/blocks/[slug]/page.tsx +39 -0
- package/templates/app/devtools/blocks/page.tsx +31 -0
- package/templates/app/devtools/config/page.tsx +31 -0
- package/templates/app/devtools/features/page.tsx +31 -0
- package/templates/app/devtools/flows/page.tsx +31 -0
- package/templates/app/devtools/layout.tsx +58 -0
- package/templates/app/devtools/page.tsx +121 -0
- package/templates/app/devtools/scheduled-actions/page.tsx +157 -0
- package/templates/app/devtools/style/page.tsx +330 -0
- package/templates/app/devtools/tags/page.tsx +31 -0
- package/templates/app/devtools/tests/[[...path]]/page.tsx +47 -0
- package/templates/app/favicon.ico +0 -0
- package/templates/app/globals.css +12 -0
- package/templates/app/layout.tsx +96 -0
- package/templates/app/public/page.tsx +30 -0
- package/templates/app/superadmin/docs/[section]/[page]/page.tsx +92 -0
- package/templates/app/superadmin/docs/page.tsx +75 -0
- package/templates/app/superadmin/layout.tsx +67 -0
- package/templates/app/superadmin/page.tsx +149 -0
- package/templates/app/superadmin/subscriptions/page.tsx +655 -0
- package/templates/app/superadmin/team-roles/page.tsx +493 -0
- package/templates/app/superadmin/teams/[teamId]/page.tsx +687 -0
- package/templates/app/superadmin/teams/page.tsx +302 -0
- package/templates/app/superadmin/users/[userId]/page.tsx +548 -0
- package/templates/app/superadmin/users/page.tsx +528 -0
|
@@ -0,0 +1,444 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Patterns List Page
|
|
3
|
+
*
|
|
4
|
+
* Custom patterns list page with quickAction to view usage reports.
|
|
5
|
+
* Uses EntityTable directly to enable custom quickActions.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
'use client'
|
|
9
|
+
|
|
10
|
+
import React, { useState, useEffect, useCallback, useMemo, useRef } from 'react'
|
|
11
|
+
import Link from 'next/link'
|
|
12
|
+
import { useRouter } from 'next/navigation'
|
|
13
|
+
import { Plus, Loader2, BarChart3, Pencil, Trash2 } from 'lucide-react'
|
|
14
|
+
import { EntityTable } from '@nextsparkjs/core/components/entities/EntityTable'
|
|
15
|
+
import { EntityBulkActions } from '@nextsparkjs/core/components/entities/EntityBulkActions'
|
|
16
|
+
import { Alert, AlertDescription } from '@nextsparkjs/core/components/ui/alert'
|
|
17
|
+
import { Button } from '@nextsparkjs/core/components/ui/button'
|
|
18
|
+
import { SkeletonEntityList } from '@nextsparkjs/core/components/ui/skeleton-list'
|
|
19
|
+
import { SearchInput } from '@nextsparkjs/core/components/shared/SearchInput'
|
|
20
|
+
import { MultiSelectFilter } from '@nextsparkjs/core/components/shared/MultiSelectFilter'
|
|
21
|
+
import { PatternDeleteDialog } from '@nextsparkjs/core/components/patterns'
|
|
22
|
+
import { useEntityConfig } from '@nextsparkjs/core/hooks/useEntityConfig'
|
|
23
|
+
import { useUrlFilters, type FilterSchema, type EntityFiltersReturn } from '@nextsparkjs/core/hooks/useUrlFilters'
|
|
24
|
+
import { listEntityData, deleteEntityData } from '@nextsparkjs/core/lib/api/entities'
|
|
25
|
+
import { useTeam } from '@nextsparkjs/core/hooks/useTeam'
|
|
26
|
+
import { usePermission } from '@nextsparkjs/core/lib/permissions/hooks'
|
|
27
|
+
import type { Permission } from '@nextsparkjs/core/lib/permissions/types'
|
|
28
|
+
import type { QuickAction, DropdownAction } from '@nextsparkjs/core/components/entities/entity-table.types'
|
|
29
|
+
import { sel } from '@nextsparkjs/core/lib/test'
|
|
30
|
+
import { toast } from 'sonner'
|
|
31
|
+
|
|
32
|
+
interface PatternItem {
|
|
33
|
+
id: string
|
|
34
|
+
name?: string
|
|
35
|
+
title?: string
|
|
36
|
+
usageCount?: number
|
|
37
|
+
[key: string]: unknown
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export default function PatternsListPage() {
|
|
41
|
+
const entityType = 'patterns'
|
|
42
|
+
const router = useRouter()
|
|
43
|
+
|
|
44
|
+
// Use the new centralized hook for entity configuration
|
|
45
|
+
const { config: entityConfig, isLoading, error: configError, isOverride } = useEntityConfig(entityType)
|
|
46
|
+
|
|
47
|
+
// Get current team for relation resolution
|
|
48
|
+
const { teamId } = useTeam()
|
|
49
|
+
|
|
50
|
+
// Check permissions
|
|
51
|
+
const canCreate = usePermission(`${entityType}.create` as Permission)
|
|
52
|
+
|
|
53
|
+
// Generate filter schema dynamically from entityConfig
|
|
54
|
+
const filterSchema = useMemo(() => {
|
|
55
|
+
const schema: FilterSchema = {
|
|
56
|
+
search: { type: 'search', urlParam: 'search' } as const
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Add filters from entity config
|
|
60
|
+
entityConfig?.ui.dashboard.filters?.forEach(filterConfig => {
|
|
61
|
+
if (filterConfig.type === 'multiSelect' || filterConfig.type === 'singleSelect') {
|
|
62
|
+
schema[filterConfig.field] = {
|
|
63
|
+
type: filterConfig.type,
|
|
64
|
+
urlParam: filterConfig.urlParam || filterConfig.field
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
return schema
|
|
70
|
+
}, [entityConfig?.ui.dashboard.filters])
|
|
71
|
+
|
|
72
|
+
// Use URL-synchronized filters with dynamic schema
|
|
73
|
+
const { filters, setFilter } = useUrlFilters(filterSchema) as unknown as EntityFiltersReturn
|
|
74
|
+
|
|
75
|
+
// Data loading states
|
|
76
|
+
const [data, setData] = useState<PatternItem[]>([])
|
|
77
|
+
const [isInitialLoad, setIsInitialLoad] = useState(true)
|
|
78
|
+
const [isSearching, setIsSearching] = useState(false)
|
|
79
|
+
const [dataError, setDataError] = useState<string | null>(null)
|
|
80
|
+
|
|
81
|
+
// Selection state for bulk actions
|
|
82
|
+
const [selectedIds, setSelectedIds] = useState<Set<string>>(new Set())
|
|
83
|
+
|
|
84
|
+
// Delete dialog state for PatternDeleteDialog
|
|
85
|
+
const [deleteTarget, setDeleteTarget] = useState<PatternItem | null>(null)
|
|
86
|
+
const [isDeleteDialogOpen, setIsDeleteDialogOpen] = useState(false)
|
|
87
|
+
const [isDeleting, setIsDeleting] = useState(false)
|
|
88
|
+
|
|
89
|
+
// Build API filters from URL filter state
|
|
90
|
+
const buildApiFilters = useCallback(() => {
|
|
91
|
+
const apiFilters: Record<string, string> = {}
|
|
92
|
+
|
|
93
|
+
if (filters.search && typeof filters.search === 'string' && filters.search.trim()) {
|
|
94
|
+
apiFilters.search = filters.search
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
entityConfig?.ui.dashboard.filters?.forEach(filterConfig => {
|
|
98
|
+
const value = filters[filterConfig.field]
|
|
99
|
+
if (Array.isArray(value) && value.length > 0) {
|
|
100
|
+
apiFilters[filterConfig.field] = value.join(',')
|
|
101
|
+
} else if (typeof value === 'string' && value) {
|
|
102
|
+
apiFilters[filterConfig.field] = value
|
|
103
|
+
}
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
return apiFilters
|
|
107
|
+
}, [filters, entityConfig?.ui.dashboard.filters])
|
|
108
|
+
|
|
109
|
+
// Load entity data function
|
|
110
|
+
const loadData = useCallback(async (isInitial = false) => {
|
|
111
|
+
if (!entityConfig) return
|
|
112
|
+
if (!entityConfig.enabled) {
|
|
113
|
+
setDataError(`Entity "${entityType}" is disabled`)
|
|
114
|
+
return
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
try {
|
|
118
|
+
if (isInitial) {
|
|
119
|
+
setIsInitialLoad(true)
|
|
120
|
+
} else {
|
|
121
|
+
setIsSearching(true)
|
|
122
|
+
}
|
|
123
|
+
setDataError(null)
|
|
124
|
+
|
|
125
|
+
const apiFilters = buildApiFilters()
|
|
126
|
+
const result = await listEntityData(entityType, {
|
|
127
|
+
limit: 50,
|
|
128
|
+
includeMeta: true,
|
|
129
|
+
...(Object.keys(apiFilters).length > 0 && { filters: apiFilters })
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
setData(result.data as PatternItem[])
|
|
133
|
+
setDataError(null)
|
|
134
|
+
} catch (err) {
|
|
135
|
+
console.error(`[PatternsListPage] Error loading data:`, err)
|
|
136
|
+
setDataError(`Error loading data: ${err instanceof Error ? err.message : 'Unknown error'}`)
|
|
137
|
+
setData([])
|
|
138
|
+
} finally {
|
|
139
|
+
setIsInitialLoad(false)
|
|
140
|
+
setIsSearching(false)
|
|
141
|
+
}
|
|
142
|
+
}, [entityConfig, entityType, buildApiFilters])
|
|
143
|
+
|
|
144
|
+
// Track previous filters and schema to detect changes
|
|
145
|
+
const filtersKey = JSON.stringify(filters)
|
|
146
|
+
const prevFiltersKeyRef = useRef<string>('')
|
|
147
|
+
const hasLoadedRef = useRef(false)
|
|
148
|
+
const schemaKeysForSync = useMemo(
|
|
149
|
+
() => Object.keys(filterSchema).sort().join(','),
|
|
150
|
+
[filterSchema]
|
|
151
|
+
)
|
|
152
|
+
const prevSchemaKeysRef = useRef<string>(schemaKeysForSync)
|
|
153
|
+
|
|
154
|
+
// Check if filters are synced with URL params
|
|
155
|
+
const filtersMatchUrl = useMemo(() => {
|
|
156
|
+
if (typeof window === 'undefined') return true
|
|
157
|
+
|
|
158
|
+
if (schemaKeysForSync !== prevSchemaKeysRef.current) {
|
|
159
|
+
prevSchemaKeysRef.current = schemaKeysForSync
|
|
160
|
+
return false
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const urlParams = new URLSearchParams(window.location.search)
|
|
164
|
+
|
|
165
|
+
for (const filterConfig of entityConfig?.ui.dashboard.filters || []) {
|
|
166
|
+
const urlValue = urlParams.get(filterConfig.field)
|
|
167
|
+
const filterValue = filters[filterConfig.field]
|
|
168
|
+
|
|
169
|
+
if (urlValue && (!filterValue || (Array.isArray(filterValue) && filterValue.length === 0))) {
|
|
170
|
+
return false
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
return true
|
|
174
|
+
}, [filters, entityConfig?.ui.dashboard.filters, schemaKeysForSync])
|
|
175
|
+
|
|
176
|
+
// Load data when config is ready AND filters are synced with URL
|
|
177
|
+
useEffect(() => {
|
|
178
|
+
if (!entityConfig?.slug) return
|
|
179
|
+
if (!filtersMatchUrl) return
|
|
180
|
+
|
|
181
|
+
const isInitial = !hasLoadedRef.current
|
|
182
|
+
const filtersChanged = prevFiltersKeyRef.current !== filtersKey
|
|
183
|
+
|
|
184
|
+
if (isInitial || filtersChanged) {
|
|
185
|
+
prevFiltersKeyRef.current = filtersKey
|
|
186
|
+
hasLoadedRef.current = true
|
|
187
|
+
loadData(isInitial)
|
|
188
|
+
}
|
|
189
|
+
}, [entityConfig?.slug, filtersKey, filtersMatchUrl, loadData])
|
|
190
|
+
|
|
191
|
+
// Handle search input change
|
|
192
|
+
const handleSearch = useCallback((query: string) => {
|
|
193
|
+
setFilter('search', query)
|
|
194
|
+
}, [setFilter])
|
|
195
|
+
|
|
196
|
+
// Handler that opens the delete dialog (called from dropdown action)
|
|
197
|
+
const handleDeleteClick = useCallback((item: PatternItem) => {
|
|
198
|
+
setDeleteTarget(item)
|
|
199
|
+
setIsDeleteDialogOpen(true)
|
|
200
|
+
}, [])
|
|
201
|
+
|
|
202
|
+
// Handler that executes the delete (called from PatternDeleteDialog)
|
|
203
|
+
const handleConfirmDelete = useCallback(async () => {
|
|
204
|
+
if (!deleteTarget) return
|
|
205
|
+
setIsDeleting(true)
|
|
206
|
+
try {
|
|
207
|
+
await deleteEntityData(entityType, deleteTarget.id)
|
|
208
|
+
toast.success(`Pattern deleted successfully`)
|
|
209
|
+
setSelectedIds(prev => {
|
|
210
|
+
const newSet = new Set(prev)
|
|
211
|
+
newSet.delete(deleteTarget.id)
|
|
212
|
+
return newSet
|
|
213
|
+
})
|
|
214
|
+
await loadData(false)
|
|
215
|
+
} catch (err) {
|
|
216
|
+
console.error(`[PatternsListPage] Error deleting pattern:`, err)
|
|
217
|
+
toast.error(`Error deleting: ${err instanceof Error ? err.message : 'Unknown error'}`)
|
|
218
|
+
} finally {
|
|
219
|
+
setIsDeleting(false)
|
|
220
|
+
setIsDeleteDialogOpen(false)
|
|
221
|
+
setDeleteTarget(null)
|
|
222
|
+
}
|
|
223
|
+
}, [deleteTarget, entityType, loadData])
|
|
224
|
+
|
|
225
|
+
// Handle bulk delete
|
|
226
|
+
const handleBulkDelete = useCallback(async (ids: string[]) => {
|
|
227
|
+
try {
|
|
228
|
+
await Promise.all(ids.map(id => deleteEntityData(entityType, id)))
|
|
229
|
+
toast.success(`${ids.length} pattern${ids.length === 1 ? '' : 's'} deleted successfully`)
|
|
230
|
+
await loadData(false)
|
|
231
|
+
} catch (err) {
|
|
232
|
+
console.error(`[PatternsListPage] Error bulk deleting patterns:`, err)
|
|
233
|
+
toast.error(`Error deleting: ${err instanceof Error ? err.message : 'Unknown error'}`)
|
|
234
|
+
throw err
|
|
235
|
+
}
|
|
236
|
+
}, [entityType, loadData])
|
|
237
|
+
|
|
238
|
+
// Handle select all
|
|
239
|
+
const handleSelectAll = useCallback(() => {
|
|
240
|
+
const allIds = new Set(data.map(item => String(item.id)))
|
|
241
|
+
setSelectedIds(allIds)
|
|
242
|
+
}, [data])
|
|
243
|
+
|
|
244
|
+
// Handle clear selection
|
|
245
|
+
const handleClearSelection = useCallback(() => {
|
|
246
|
+
setSelectedIds(new Set())
|
|
247
|
+
}, [])
|
|
248
|
+
|
|
249
|
+
// Get item name for confirmations
|
|
250
|
+
const getItemName = useCallback((item: PatternItem): string => {
|
|
251
|
+
return String(item.name || item.title || item.id)
|
|
252
|
+
}, [])
|
|
253
|
+
|
|
254
|
+
// Custom quickActions for patterns - includes "View Usages" action
|
|
255
|
+
const quickActions: QuickAction<PatternItem>[] = useMemo(
|
|
256
|
+
() => [
|
|
257
|
+
{
|
|
258
|
+
id: 'view-usages',
|
|
259
|
+
label: 'View Usages',
|
|
260
|
+
icon: <BarChart3 className="h-4 w-4" />,
|
|
261
|
+
onClick: (item: PatternItem) => {
|
|
262
|
+
router.push(`/dashboard/patterns/${item.id}/reports`)
|
|
263
|
+
},
|
|
264
|
+
dataCySuffix: 'usages',
|
|
265
|
+
},
|
|
266
|
+
],
|
|
267
|
+
[router]
|
|
268
|
+
)
|
|
269
|
+
|
|
270
|
+
// Custom dropdownActions - replace default delete with PatternDeleteDialog
|
|
271
|
+
const dropdownActions: DropdownAction<PatternItem>[] = useMemo(
|
|
272
|
+
() => [
|
|
273
|
+
{
|
|
274
|
+
id: 'edit',
|
|
275
|
+
label: 'Edit',
|
|
276
|
+
icon: <Pencil className="h-4 w-4" />,
|
|
277
|
+
onClick: (item) => router.push(`/dashboard/patterns/${item.id}/edit`),
|
|
278
|
+
dataCySuffix: 'edit',
|
|
279
|
+
},
|
|
280
|
+
{
|
|
281
|
+
id: 'delete',
|
|
282
|
+
label: 'Delete',
|
|
283
|
+
icon: <Trash2 className="h-4 w-4" />,
|
|
284
|
+
onClick: handleDeleteClick,
|
|
285
|
+
variant: 'destructive',
|
|
286
|
+
separator: true,
|
|
287
|
+
dataCySuffix: 'delete',
|
|
288
|
+
},
|
|
289
|
+
],
|
|
290
|
+
[router, handleDeleteClick]
|
|
291
|
+
)
|
|
292
|
+
|
|
293
|
+
if (isLoading) {
|
|
294
|
+
return <SkeletonEntityList />
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
if (configError || !entityConfig) {
|
|
298
|
+
return (
|
|
299
|
+
<Alert>
|
|
300
|
+
<AlertDescription>
|
|
301
|
+
{configError || `Could not load configuration for entity "${entityType}".`}
|
|
302
|
+
</AlertDescription>
|
|
303
|
+
</Alert>
|
|
304
|
+
)
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
if (dataError) {
|
|
308
|
+
return (
|
|
309
|
+
<Alert>
|
|
310
|
+
<AlertDescription>
|
|
311
|
+
{dataError}
|
|
312
|
+
</AlertDescription>
|
|
313
|
+
</Alert>
|
|
314
|
+
)
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
const bulkOperationsEnabled = entityConfig.ui.features.bulkOperations
|
|
318
|
+
const enableSearch = entityConfig.ui.features.searchable
|
|
319
|
+
const enableFilters = entityConfig.ui.features.filterable && entityConfig.ui.dashboard.filters?.length
|
|
320
|
+
const searchValue = typeof filters.search === 'string' ? filters.search : ''
|
|
321
|
+
|
|
322
|
+
return (
|
|
323
|
+
<div data-cy={sel('entities.page.container', { slug: entityConfig.slug })}>
|
|
324
|
+
<div className="p-6 space-y-6" data-cy={sel('entities.list.container', { slug: entityConfig.slug })}>
|
|
325
|
+
{/* Row 1: Title + Create button */}
|
|
326
|
+
<div className="flex items-center justify-between">
|
|
327
|
+
<div className="space-y-1">
|
|
328
|
+
<h1 className="text-2xl font-bold tracking-tight" data-cy={sel('entities.header.title', { slug: entityConfig.slug })}>
|
|
329
|
+
{entityConfig.names.plural}
|
|
330
|
+
</h1>
|
|
331
|
+
<p className="text-muted-foreground">
|
|
332
|
+
Manage your {entityConfig.names.plural.toLowerCase()}
|
|
333
|
+
</p>
|
|
334
|
+
</div>
|
|
335
|
+
<div className="flex items-center gap-2">
|
|
336
|
+
{canCreate && (
|
|
337
|
+
<Button asChild data-cy={sel('entities.list.addButton', { slug: entityConfig.slug })}>
|
|
338
|
+
<Link href={`/dashboard/${entityConfig.slug}/create`}>
|
|
339
|
+
<Plus className="h-4 w-4 mr-2" />
|
|
340
|
+
Add {entityConfig.names.singular}
|
|
341
|
+
</Link>
|
|
342
|
+
</Button>
|
|
343
|
+
)}
|
|
344
|
+
</div>
|
|
345
|
+
</div>
|
|
346
|
+
|
|
347
|
+
{/* Row 2: Search + Filters */}
|
|
348
|
+
{(enableSearch || enableFilters) && (
|
|
349
|
+
<div className="flex flex-col sm:flex-row gap-3 items-center flex-wrap">
|
|
350
|
+
{enableSearch && (
|
|
351
|
+
<SearchInput
|
|
352
|
+
placeholder={`Search ${entityConfig.names.plural.toLowerCase()}...`}
|
|
353
|
+
value={searchValue}
|
|
354
|
+
onChange={(e) => handleSearch(e.target.value)}
|
|
355
|
+
containerClassName="flex-1 max-w-md"
|
|
356
|
+
data-cy={sel('entities.list.search.container', { slug: entityConfig.slug })}
|
|
357
|
+
/>
|
|
358
|
+
)}
|
|
359
|
+
|
|
360
|
+
{enableFilters && entityConfig.ui.dashboard.filters?.map(filterConfig => {
|
|
361
|
+
const field = entityConfig.fields.find(f => f.name === filterConfig.field)
|
|
362
|
+
if (!field?.options) return null
|
|
363
|
+
|
|
364
|
+
const filterValues = filters[filterConfig.field]
|
|
365
|
+
const values = Array.isArray(filterValues) ? filterValues : []
|
|
366
|
+
|
|
367
|
+
return (
|
|
368
|
+
<MultiSelectFilter
|
|
369
|
+
key={filterConfig.field}
|
|
370
|
+
label={filterConfig.label || field.display.label}
|
|
371
|
+
options={field.options.map(opt => ({
|
|
372
|
+
value: String(opt.value),
|
|
373
|
+
label: opt.label
|
|
374
|
+
}))}
|
|
375
|
+
values={values}
|
|
376
|
+
onChange={(newValues) => setFilter(filterConfig.field, newValues)}
|
|
377
|
+
data-cy={sel('entities.list.filters.trigger', { slug: entityConfig.slug, field: filterConfig.field })}
|
|
378
|
+
/>
|
|
379
|
+
)
|
|
380
|
+
})}
|
|
381
|
+
|
|
382
|
+
{isSearching && (
|
|
383
|
+
<Loader2 className="h-4 w-4 animate-spin text-muted-foreground" />
|
|
384
|
+
)}
|
|
385
|
+
</div>
|
|
386
|
+
)}
|
|
387
|
+
|
|
388
|
+
{/* Row 3: Data Table with custom quickActions and dropdownActions */}
|
|
389
|
+
<EntityTable
|
|
390
|
+
entityConfig={entityConfig}
|
|
391
|
+
data={data as Array<{ id: string }>}
|
|
392
|
+
loading={isInitialLoad}
|
|
393
|
+
selectable={bulkOperationsEnabled}
|
|
394
|
+
selectedIds={selectedIds}
|
|
395
|
+
onSelectionChange={setSelectedIds}
|
|
396
|
+
enableSearch={false}
|
|
397
|
+
searchQuery={searchValue}
|
|
398
|
+
onSearch={handleSearch}
|
|
399
|
+
getItemName={getItemName}
|
|
400
|
+
teamId={teamId}
|
|
401
|
+
useDefaultActions={false}
|
|
402
|
+
quickActions={quickActions}
|
|
403
|
+
dropdownActions={dropdownActions}
|
|
404
|
+
showHeader={false}
|
|
405
|
+
pagination={{
|
|
406
|
+
pageSize: 10,
|
|
407
|
+
showPageSizeSelector: true,
|
|
408
|
+
pageSizeOptions: [10, 20, 50, 100],
|
|
409
|
+
}}
|
|
410
|
+
/>
|
|
411
|
+
|
|
412
|
+
{/* Floating Bulk Actions Bar */}
|
|
413
|
+
{bulkOperationsEnabled && (
|
|
414
|
+
<EntityBulkActions
|
|
415
|
+
entitySlug={entityConfig.slug}
|
|
416
|
+
selectedIds={selectedIds}
|
|
417
|
+
onClearSelection={handleClearSelection}
|
|
418
|
+
config={{
|
|
419
|
+
enableSelectAll: true,
|
|
420
|
+
totalItems: data.length,
|
|
421
|
+
onSelectAll: handleSelectAll,
|
|
422
|
+
enableDelete: true,
|
|
423
|
+
onDelete: handleBulkDelete,
|
|
424
|
+
itemLabel: entityConfig.names.singular,
|
|
425
|
+
itemLabelPlural: entityConfig.names.plural,
|
|
426
|
+
}}
|
|
427
|
+
/>
|
|
428
|
+
)}
|
|
429
|
+
|
|
430
|
+
{/* Pattern Delete Dialog - shows usage warning before deleting */}
|
|
431
|
+
{deleteTarget && (
|
|
432
|
+
<PatternDeleteDialog
|
|
433
|
+
patternId={deleteTarget.id}
|
|
434
|
+
patternTitle={getItemName(deleteTarget)}
|
|
435
|
+
onConfirm={handleConfirmDelete}
|
|
436
|
+
isDeleting={isDeleting}
|
|
437
|
+
open={isDeleteDialogOpen}
|
|
438
|
+
onOpenChange={setIsDeleteDialogOpen}
|
|
439
|
+
/>
|
|
440
|
+
)}
|
|
441
|
+
</div>
|
|
442
|
+
</div>
|
|
443
|
+
)
|
|
444
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { FeatureGate } from '@nextsparkjs/core/components/billing/FeatureGate'
|
|
4
|
+
import { FeaturePlaceholder } from '@nextsparkjs/core/components/billing/FeaturePlaceholder'
|
|
5
|
+
import { BarChart3 } from 'lucide-react'
|
|
6
|
+
import { useTranslations } from 'next-intl'
|
|
7
|
+
|
|
8
|
+
export default function AdvancedAnalyticsPage() {
|
|
9
|
+
const t = useTranslations('features')
|
|
10
|
+
|
|
11
|
+
return (
|
|
12
|
+
<div data-cy="feature-analytics-page">
|
|
13
|
+
<FeatureGate
|
|
14
|
+
feature="advanced_analytics"
|
|
15
|
+
fallback={
|
|
16
|
+
<FeaturePlaceholder
|
|
17
|
+
feature="advanced_analytics"
|
|
18
|
+
icon={<BarChart3 className="h-8 w-8" />}
|
|
19
|
+
benefits={[
|
|
20
|
+
t('analytics.benefit1'),
|
|
21
|
+
t('analytics.benefit2'),
|
|
22
|
+
t('analytics.benefit3'),
|
|
23
|
+
]}
|
|
24
|
+
/>
|
|
25
|
+
}
|
|
26
|
+
>
|
|
27
|
+
{/* Contenido real cuando se implemente */}
|
|
28
|
+
<div data-cy="analytics-content">
|
|
29
|
+
<h1 className="text-2xl font-bold">{t('analytics.title')}</h1>
|
|
30
|
+
<p className="text-muted-foreground mt-2">Analytics dashboard coming soon...</p>
|
|
31
|
+
</div>
|
|
32
|
+
</FeatureGate>
|
|
33
|
+
</div>
|
|
34
|
+
)
|
|
35
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { FeatureGate } from '@nextsparkjs/core/components/billing/FeatureGate'
|
|
4
|
+
import { FeaturePlaceholder } from '@nextsparkjs/core/components/billing/FeaturePlaceholder'
|
|
5
|
+
import { Zap } from 'lucide-react'
|
|
6
|
+
import { useTranslations } from 'next-intl'
|
|
7
|
+
|
|
8
|
+
export default function TaskAutomationPage() {
|
|
9
|
+
const t = useTranslations('features')
|
|
10
|
+
|
|
11
|
+
return (
|
|
12
|
+
<div data-cy="feature-automation-page">
|
|
13
|
+
<FeatureGate
|
|
14
|
+
feature="task_automation"
|
|
15
|
+
fallback={
|
|
16
|
+
<FeaturePlaceholder
|
|
17
|
+
feature="task_automation"
|
|
18
|
+
icon={<Zap className="h-8 w-8" />}
|
|
19
|
+
benefits={[
|
|
20
|
+
t('automation.benefit1'),
|
|
21
|
+
t('automation.benefit2'),
|
|
22
|
+
t('automation.benefit3'),
|
|
23
|
+
]}
|
|
24
|
+
/>
|
|
25
|
+
}
|
|
26
|
+
>
|
|
27
|
+
{/* Contenido real cuando se implemente */}
|
|
28
|
+
<div data-cy="automation-content">
|
|
29
|
+
<h1 className="text-2xl font-bold">{t('automation.title')}</h1>
|
|
30
|
+
<p className="text-muted-foreground mt-2">Task automation coming soon...</p>
|
|
31
|
+
</div>
|
|
32
|
+
</FeatureGate>
|
|
33
|
+
</div>
|
|
34
|
+
)
|
|
35
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { ReactNode } from 'react'
|
|
2
|
+
|
|
3
|
+
interface FeaturesLayoutProps {
|
|
4
|
+
children: ReactNode
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export default function FeaturesLayout({ children }: FeaturesLayoutProps) {
|
|
8
|
+
return (
|
|
9
|
+
<div className="container py-8" data-cy="features-layout">
|
|
10
|
+
{children}
|
|
11
|
+
</div>
|
|
12
|
+
)
|
|
13
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { FeatureGate } from '@nextsparkjs/core/components/billing/FeatureGate'
|
|
4
|
+
import { FeaturePlaceholder } from '@nextsparkjs/core/components/billing/FeaturePlaceholder'
|
|
5
|
+
import { Webhook } from 'lucide-react'
|
|
6
|
+
import { useTranslations } from 'next-intl'
|
|
7
|
+
|
|
8
|
+
export default function WebhooksPage() {
|
|
9
|
+
const t = useTranslations('features')
|
|
10
|
+
|
|
11
|
+
return (
|
|
12
|
+
<div data-cy="feature-webhooks-page">
|
|
13
|
+
<FeatureGate
|
|
14
|
+
feature="webhooks"
|
|
15
|
+
fallback={
|
|
16
|
+
<FeaturePlaceholder
|
|
17
|
+
feature="webhooks"
|
|
18
|
+
icon={<Webhook className="h-8 w-8" />}
|
|
19
|
+
benefits={[
|
|
20
|
+
t('webhooks.benefit1'),
|
|
21
|
+
t('webhooks.benefit2'),
|
|
22
|
+
t('webhooks.benefit3'),
|
|
23
|
+
]}
|
|
24
|
+
/>
|
|
25
|
+
}
|
|
26
|
+
>
|
|
27
|
+
{/* Contenido real cuando se implemente */}
|
|
28
|
+
<div data-cy="webhooks-content">
|
|
29
|
+
<h1 className="text-2xl font-bold">{t('webhooks.title')}</h1>
|
|
30
|
+
<p className="text-muted-foreground mt-2">Webhooks configuration coming soon...</p>
|
|
31
|
+
</div>
|
|
32
|
+
</FeatureGate>
|
|
33
|
+
</div>
|
|
34
|
+
)
|
|
35
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { useAuth } from '@nextsparkjs/core/hooks/useAuth'
|
|
4
|
+
import { useRouter } from 'next/navigation'
|
|
5
|
+
import { useEffect, Suspense } from 'react'
|
|
6
|
+
import { Loader2 } from 'lucide-react'
|
|
7
|
+
import { DashboardTranslationPreloader } from '@nextsparkjs/core/lib/i18n/DashboardTranslationPreloader'
|
|
8
|
+
import { TranslationDebugger } from '@nextsparkjs/core/utils/dev/TranslationDebugger'
|
|
9
|
+
import { useEnsureUserMetadata } from '@nextsparkjs/core/hooks/useEnsureUserMetadata'
|
|
10
|
+
import { useAuthMethodDetector } from '@nextsparkjs/core/hooks/useAuthMethodDetector'
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Auth Method Detector Wrapper (uses useSearchParams internally)
|
|
14
|
+
*/
|
|
15
|
+
function AuthMethodDetectorWrapper() {
|
|
16
|
+
useAuthMethodDetector()
|
|
17
|
+
return null
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Dashboard Layout Content
|
|
22
|
+
*/
|
|
23
|
+
function DashboardLayoutContent({ children }: { children: React.ReactNode }) {
|
|
24
|
+
const { user, isLoading } = useAuth()
|
|
25
|
+
const router = useRouter()
|
|
26
|
+
|
|
27
|
+
// Asegurar que el usuario tenga metadata default
|
|
28
|
+
useEnsureUserMetadata()
|
|
29
|
+
|
|
30
|
+
useEffect(() => {
|
|
31
|
+
if (!isLoading && !user) {
|
|
32
|
+
router.push('/login')
|
|
33
|
+
}
|
|
34
|
+
}, [user, isLoading, router])
|
|
35
|
+
|
|
36
|
+
if (isLoading) {
|
|
37
|
+
return (
|
|
38
|
+
<div className="min-h-screen flex items-center justify-center">
|
|
39
|
+
<Loader2 className="h-8 w-8 animate-spin text-muted-foreground" />
|
|
40
|
+
</div>
|
|
41
|
+
)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (!user) {
|
|
45
|
+
return null
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return (
|
|
49
|
+
<>
|
|
50
|
+
{/* Precargar traducciones del dashboard para mejorar UX de navegación */}
|
|
51
|
+
<DashboardTranslationPreloader key="dashboard-translation-preloader" />
|
|
52
|
+
|
|
53
|
+
{/* Debugger de traducciones (solo en desarrollo con ?debug-i18n=true) */}
|
|
54
|
+
<TranslationDebugger key="translation-debugger" />
|
|
55
|
+
|
|
56
|
+
{/* Detect and save auth method from OAuth redirects (wrapped in Suspense) */}
|
|
57
|
+
<Suspense key="auth-method-detector" fallback={null}>
|
|
58
|
+
<AuthMethodDetectorWrapper />
|
|
59
|
+
</Suspense>
|
|
60
|
+
|
|
61
|
+
{/*
|
|
62
|
+
Children run within authenticated boundary -
|
|
63
|
+
nested layouts CAN be themed but security is guaranteed
|
|
64
|
+
*/}
|
|
65
|
+
<div key="dashboard-children" data-cy="dashboard-container">{children}</div>
|
|
66
|
+
</>
|
|
67
|
+
)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* CORE SECURITY LAYOUT - NOT OVERRIDEABLE BY THEMES
|
|
72
|
+
*
|
|
73
|
+
* This layout provides essential authentication and security measures
|
|
74
|
+
* that cannot be bypassed by theme overrides. All dashboard routes
|
|
75
|
+
* run within this authenticated boundary.
|
|
76
|
+
*
|
|
77
|
+
* SECURITY: This layout is intentionally NOT using template resolver
|
|
78
|
+
* to prevent themes from bypassing authentication.
|
|
79
|
+
*/
|
|
80
|
+
export default function CoreDashboardLayout({
|
|
81
|
+
children
|
|
82
|
+
}: {
|
|
83
|
+
children: React.ReactNode
|
|
84
|
+
}) {
|
|
85
|
+
return <DashboardLayoutContent>{children}</DashboardLayoutContent>
|
|
86
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Permission Denied Page
|
|
3
|
+
*
|
|
4
|
+
* Server page that reads search params and renders the existing NoPermission component.
|
|
5
|
+
* This page is displayed when a user attempts to access a resource they don't have permission for.
|
|
6
|
+
*/
|
|
7
|
+
import { NoPermission } from '@nextsparkjs/core/components/permissions/NoPermission'
|
|
8
|
+
|
|
9
|
+
interface PermissionDeniedPageProps {
|
|
10
|
+
searchParams: Promise<{
|
|
11
|
+
entity?: string
|
|
12
|
+
action?: string
|
|
13
|
+
}>
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export default async function PermissionDeniedPage({
|
|
17
|
+
searchParams
|
|
18
|
+
}: PermissionDeniedPageProps) {
|
|
19
|
+
const { entity, action } = await searchParams
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
<NoPermission
|
|
23
|
+
entityName={entity}
|
|
24
|
+
action={action as 'list' | 'read' | 'create' | 'update' | 'delete'}
|
|
25
|
+
showBackButton={true}
|
|
26
|
+
showHomeButton={true}
|
|
27
|
+
/>
|
|
28
|
+
)
|
|
29
|
+
}
|