@contractspec/example.saas-boilerplate 3.7.5 → 3.7.7

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.
Files changed (115) hide show
  1. package/.turbo/turbo-build.log +8 -8
  2. package/AGENTS.md +50 -27
  3. package/CHANGELOG.md +16 -0
  4. package/README.md +64 -144
  5. package/dist/billing/billing.event.js +1 -1
  6. package/dist/billing/index.d.ts +6 -6
  7. package/dist/billing/index.js +1 -1
  8. package/dist/browser/billing/billing.event.js +1 -1
  9. package/dist/browser/billing/index.js +1 -1
  10. package/dist/browser/index.js +931 -932
  11. package/dist/browser/project/index.js +209 -209
  12. package/dist/browser/project/project.event.js +1 -1
  13. package/dist/browser/ui/SaasDashboard.js +45 -45
  14. package/dist/browser/ui/SaasProjectList.js +7 -7
  15. package/dist/browser/ui/SaasSettingsPanel.js +12 -12
  16. package/dist/browser/ui/hooks/index.js +2 -2
  17. package/dist/browser/ui/hooks/useProjectList.js +1 -1
  18. package/dist/browser/ui/hooks/useProjectMutations.js +1 -1
  19. package/dist/browser/ui/index.js +483 -484
  20. package/dist/browser/ui/modals/CreateProjectModal.js +10 -10
  21. package/dist/browser/ui/modals/ProjectActionsModal.js +13 -13
  22. package/dist/browser/ui/modals/index.js +23 -23
  23. package/dist/browser/ui/renderers/index.js +112 -112
  24. package/dist/browser/ui/renderers/project-list.renderer.js +7 -7
  25. package/dist/handlers/index.d.ts +2 -2
  26. package/dist/index.d.ts +4 -4
  27. package/dist/index.js +931 -932
  28. package/dist/node/billing/billing.event.js +1 -1
  29. package/dist/node/billing/index.js +1 -1
  30. package/dist/node/index.js +931 -932
  31. package/dist/node/project/index.js +209 -209
  32. package/dist/node/project/project.event.js +1 -1
  33. package/dist/node/ui/SaasDashboard.js +45 -45
  34. package/dist/node/ui/SaasProjectList.js +7 -7
  35. package/dist/node/ui/SaasSettingsPanel.js +12 -12
  36. package/dist/node/ui/hooks/index.js +2 -2
  37. package/dist/node/ui/hooks/useProjectList.js +1 -1
  38. package/dist/node/ui/hooks/useProjectMutations.js +1 -1
  39. package/dist/node/ui/index.js +483 -484
  40. package/dist/node/ui/modals/CreateProjectModal.js +10 -10
  41. package/dist/node/ui/modals/ProjectActionsModal.js +13 -13
  42. package/dist/node/ui/modals/index.js +23 -23
  43. package/dist/node/ui/renderers/index.js +112 -112
  44. package/dist/node/ui/renderers/project-list.renderer.js +7 -7
  45. package/dist/presentations/index.d.ts +1 -1
  46. package/dist/project/index.d.ts +7 -7
  47. package/dist/project/index.js +209 -209
  48. package/dist/project/project.event.js +1 -1
  49. package/dist/settings/index.d.ts +1 -1
  50. package/dist/ui/SaasDashboard.js +45 -45
  51. package/dist/ui/SaasProjectList.js +7 -7
  52. package/dist/ui/SaasSettingsPanel.js +12 -12
  53. package/dist/ui/hooks/index.d.ts +2 -2
  54. package/dist/ui/hooks/index.js +2 -2
  55. package/dist/ui/hooks/useProjectList.d.ts +5 -0
  56. package/dist/ui/hooks/useProjectList.js +1 -1
  57. package/dist/ui/hooks/useProjectMutations.d.ts +8 -0
  58. package/dist/ui/hooks/useProjectMutations.js +1 -1
  59. package/dist/ui/index.d.ts +4 -4
  60. package/dist/ui/index.js +483 -484
  61. package/dist/ui/modals/CreateProjectModal.js +10 -10
  62. package/dist/ui/modals/ProjectActionsModal.js +13 -13
  63. package/dist/ui/modals/index.js +23 -23
  64. package/dist/ui/renderers/index.d.ts +1 -1
  65. package/dist/ui/renderers/index.js +112 -112
  66. package/dist/ui/renderers/project-list.renderer.d.ts +1 -1
  67. package/dist/ui/renderers/project-list.renderer.js +7 -7
  68. package/package.json +14 -14
  69. package/src/billing/billing.entity.ts +132 -132
  70. package/src/billing/billing.enum.ts +9 -9
  71. package/src/billing/billing.event.ts +71 -71
  72. package/src/billing/billing.handler.ts +87 -87
  73. package/src/billing/billing.operations.ts +158 -158
  74. package/src/billing/billing.presentation.ts +45 -45
  75. package/src/billing/billing.schema.ts +76 -76
  76. package/src/billing/index.ts +43 -48
  77. package/src/dashboard/dashboard.presentation.ts +45 -45
  78. package/src/dashboard/index.ts +2 -2
  79. package/src/docs/saas-boilerplate.docblock.ts +43 -43
  80. package/src/example.ts +32 -32
  81. package/src/handlers/index.ts +9 -9
  82. package/src/handlers/saas.handlers.ts +250 -249
  83. package/src/index.ts +40 -41
  84. package/src/presentations/index.ts +18 -20
  85. package/src/project/index.ts +45 -50
  86. package/src/project/project.entity.ts +68 -68
  87. package/src/project/project.enum.ts +8 -8
  88. package/src/project/project.event.ts +79 -79
  89. package/src/project/project.handler.ts +103 -103
  90. package/src/project/project.operations.ts +236 -236
  91. package/src/project/project.presentation.ts +46 -46
  92. package/src/project/project.schema.ts +90 -90
  93. package/src/saas-boilerplate.feature.ts +100 -100
  94. package/src/seeders/index.ts +20 -20
  95. package/src/settings/index.ts +2 -3
  96. package/src/settings/settings.entity.ts +65 -65
  97. package/src/settings/settings.enum.ts +4 -4
  98. package/src/shared/mock-data.ts +92 -92
  99. package/src/shared/overlay-types.ts +23 -23
  100. package/src/tests/operations.test-spec.ts +96 -96
  101. package/src/ui/SaasDashboard.tsx +270 -270
  102. package/src/ui/SaasProjectList.tsx +90 -90
  103. package/src/ui/SaasSettingsPanel.tsx +84 -84
  104. package/src/ui/hooks/index.ts +3 -3
  105. package/src/ui/hooks/useProjectList.ts +69 -68
  106. package/src/ui/hooks/useProjectMutations.ts +144 -143
  107. package/src/ui/index.ts +8 -12
  108. package/src/ui/modals/CreateProjectModal.tsx +154 -154
  109. package/src/ui/modals/ProjectActionsModal.tsx +321 -321
  110. package/src/ui/overlays/demo-overlays.ts +49 -49
  111. package/src/ui/renderers/index.ts +5 -4
  112. package/src/ui/renderers/project-list.markdown.ts +204 -204
  113. package/src/ui/renderers/project-list.renderer.tsx +14 -13
  114. package/tsconfig.json +7 -8
  115. package/tsdown.config.js +7 -3
@@ -1,5 +1,15 @@
1
1
  'use client';
2
2
 
3
+ import {
4
+ Button,
5
+ EmptyState,
6
+ EntityCard,
7
+ ErrorState,
8
+ LoaderBlock,
9
+ StatCard,
10
+ StatCardGroup,
11
+ StatusChip,
12
+ } from '@contractspec/lib.design-system';
3
13
  /**
4
14
  * SaaS Dashboard
5
15
  *
@@ -11,21 +21,11 @@
11
21
  * - UpdateProjectContract -> Edit project via modal
12
22
  * - DeleteProjectContract -> Delete project via modal
13
23
  */
14
- import { useState, useCallback } from 'react';
15
- import {
16
- StatCard,
17
- StatCardGroup,
18
- StatusChip,
19
- EntityCard,
20
- EmptyState,
21
- LoaderBlock,
22
- ErrorState,
23
- Button,
24
- } from '@contractspec/lib.design-system';
24
+ import { useCallback, useState } from 'react';
25
25
  import {
26
- useProjectList,
27
- type Project,
28
- type Subscription,
26
+ type Project,
27
+ type Subscription,
28
+ useProjectList,
29
29
  } from './hooks/useProjectList';
30
30
  import { useProjectMutations } from './hooks/useProjectMutations';
31
31
  import { CreateProjectModal } from './modals/CreateProjectModal';
@@ -34,292 +34,292 @@ import { ProjectActionsModal } from './modals/ProjectActionsModal';
34
34
  type Tab = 'projects' | 'billing' | 'settings';
35
35
 
36
36
  function getStatusTone(
37
- status: Project['status']
37
+ status: Project['status']
38
38
  ): 'success' | 'warning' | 'neutral' | 'danger' {
39
- switch (status) {
40
- case 'ACTIVE':
41
- return 'success';
42
- case 'DRAFT':
43
- return 'neutral';
44
- case 'ARCHIVED':
45
- return 'warning';
46
- default:
47
- return 'neutral';
48
- }
39
+ switch (status) {
40
+ case 'ACTIVE':
41
+ return 'success';
42
+ case 'DRAFT':
43
+ return 'neutral';
44
+ case 'ARCHIVED':
45
+ return 'warning';
46
+ default:
47
+ return 'neutral';
48
+ }
49
49
  }
50
50
 
51
51
  export function SaasDashboard() {
52
- const [activeTab, setActiveTab] = useState<Tab>('projects');
53
- const [isCreateModalOpen, setIsCreateModalOpen] = useState(false);
54
- const [selectedProject, setSelectedProject] = useState<Project | null>(null);
55
- const [isProjectActionsOpen, setIsProjectActionsOpen] = useState(false);
52
+ const [activeTab, setActiveTab] = useState<Tab>('projects');
53
+ const [isCreateModalOpen, setIsCreateModalOpen] = useState(false);
54
+ const [selectedProject, setSelectedProject] = useState<Project | null>(null);
55
+ const [isProjectActionsOpen, setIsProjectActionsOpen] = useState(false);
56
56
 
57
- const { data, subscription, loading, error, stats, refetch } =
58
- useProjectList();
57
+ const { data, subscription, loading, error, stats, refetch } =
58
+ useProjectList();
59
59
 
60
- const mutations = useProjectMutations({
61
- onSuccess: () => {
62
- refetch();
63
- },
64
- });
60
+ const mutations = useProjectMutations({
61
+ onSuccess: () => {
62
+ refetch();
63
+ },
64
+ });
65
65
 
66
- const handleProjectClick = useCallback((project: Project) => {
67
- setSelectedProject(project);
68
- setIsProjectActionsOpen(true);
69
- }, []);
66
+ const handleProjectClick = useCallback((project: Project) => {
67
+ setSelectedProject(project);
68
+ setIsProjectActionsOpen(true);
69
+ }, []);
70
70
 
71
- const tabs: { id: Tab; label: string; icon: string }[] = [
72
- { id: 'projects', label: 'Projects', icon: '📁' },
73
- { id: 'billing', label: 'Billing', icon: '💳' },
74
- { id: 'settings', label: 'Settings', icon: '⚙️' },
75
- ];
71
+ const tabs: { id: Tab; label: string; icon: string }[] = [
72
+ { id: 'projects', label: 'Projects', icon: '📁' },
73
+ { id: 'billing', label: 'Billing', icon: '💳' },
74
+ { id: 'settings', label: 'Settings', icon: '⚙️' },
75
+ ];
76
76
 
77
- if (loading && !data) {
78
- return <LoaderBlock label="Loading dashboard..." />;
79
- }
77
+ if (loading && !data) {
78
+ return <LoaderBlock label="Loading dashboard..." />;
79
+ }
80
80
 
81
- if (error) {
82
- return (
83
- <ErrorState
84
- title="Failed to load dashboard"
85
- description={error.message}
86
- onRetry={refetch}
87
- retryLabel="Retry"
88
- />
89
- );
90
- }
81
+ if (error) {
82
+ return (
83
+ <ErrorState
84
+ title="Failed to load dashboard"
85
+ description={error.message}
86
+ onRetry={refetch}
87
+ retryLabel="Retry"
88
+ />
89
+ );
90
+ }
91
91
 
92
- return (
93
- <div className="space-y-6">
94
- {/* Header */}
95
- <div className="flex items-center justify-between">
96
- <h2 className="text-2xl font-bold">SaaS Dashboard</h2>
97
- {activeTab === 'projects' && (
98
- <Button onPress={() => setIsCreateModalOpen(true)}>
99
- <span className="mr-2">+</span> New Project
100
- </Button>
101
- )}
102
- </div>
92
+ return (
93
+ <div className="space-y-6">
94
+ {/* Header */}
95
+ <div className="flex items-center justify-between">
96
+ <h2 className="font-bold text-2xl">SaaS Dashboard</h2>
97
+ {activeTab === 'projects' && (
98
+ <Button onPress={() => setIsCreateModalOpen(true)}>
99
+ <span className="mr-2">+</span> New Project
100
+ </Button>
101
+ )}
102
+ </div>
103
103
 
104
- {/* Stats Row */}
105
- {stats && subscription && (
106
- <StatCardGroup>
107
- <StatCard label="Projects" value={stats.total.toString()} />
108
- <StatCard label="Active" value={stats.activeCount.toString()} />
109
- <StatCard label="Draft" value={stats.draftCount.toString()} />
110
- <StatCard
111
- label="Plan"
112
- value={subscription.plan}
113
- hint={subscription.status}
114
- />
115
- </StatCardGroup>
116
- )}
104
+ {/* Stats Row */}
105
+ {stats && subscription && (
106
+ <StatCardGroup>
107
+ <StatCard label="Projects" value={stats.total.toString()} />
108
+ <StatCard label="Active" value={stats.activeCount.toString()} />
109
+ <StatCard label="Draft" value={stats.draftCount.toString()} />
110
+ <StatCard
111
+ label="Plan"
112
+ value={subscription.plan}
113
+ hint={subscription.status}
114
+ />
115
+ </StatCardGroup>
116
+ )}
117
117
 
118
- {/* Navigation Tabs */}
119
- <nav className="bg-muted flex gap-1 rounded-lg p-1" role="tablist">
120
- {tabs.map((tab) => (
121
- <button
122
- key={tab.id}
123
- type="button"
124
- role="tab"
125
- aria-selected={activeTab === tab.id}
126
- onClick={() => setActiveTab(tab.id)}
127
- className={`flex flex-1 items-center justify-center gap-2 rounded-md px-4 py-2 text-sm font-medium transition-colors ${
128
- activeTab === tab.id
129
- ? 'bg-background text-foreground shadow-sm'
130
- : 'text-muted-foreground hover:text-foreground'
131
- }`}
132
- >
133
- <span>{tab.icon}</span>
134
- {tab.label}
135
- </button>
136
- ))}
137
- </nav>
118
+ {/* Navigation Tabs */}
119
+ <nav className="flex gap-1 rounded-lg bg-muted p-1" role="tablist">
120
+ {tabs.map((tab) => (
121
+ <button
122
+ key={tab.id}
123
+ type="button"
124
+ role="tab"
125
+ aria-selected={activeTab === tab.id}
126
+ onClick={() => setActiveTab(tab.id)}
127
+ className={`flex flex-1 items-center justify-center gap-2 rounded-md px-4 py-2 font-medium text-sm transition-colors ${
128
+ activeTab === tab.id
129
+ ? 'bg-background text-foreground shadow-sm'
130
+ : 'text-muted-foreground hover:text-foreground'
131
+ }`}
132
+ >
133
+ <span>{tab.icon}</span>
134
+ {tab.label}
135
+ </button>
136
+ ))}
137
+ </nav>
138
138
 
139
- {/* Tab Content */}
140
- <div className="min-h-[400px]" role="tabpanel">
141
- {activeTab === 'projects' && (
142
- <ProjectsTab data={data} onProjectClick={handleProjectClick} />
143
- )}
144
- {activeTab === 'billing' && <BillingTab subscription={subscription} />}
145
- {activeTab === 'settings' && <SettingsTab />}
146
- </div>
139
+ {/* Tab Content */}
140
+ <div className="min-h-[400px]" role="tabpanel">
141
+ {activeTab === 'projects' && (
142
+ <ProjectsTab data={data} onProjectClick={handleProjectClick} />
143
+ )}
144
+ {activeTab === 'billing' && <BillingTab subscription={subscription} />}
145
+ {activeTab === 'settings' && <SettingsTab />}
146
+ </div>
147
147
 
148
- {/* Create Project Modal */}
149
- <CreateProjectModal
150
- isOpen={isCreateModalOpen}
151
- onClose={() => setIsCreateModalOpen(false)}
152
- onSubmit={async (input) => {
153
- await mutations.createProject(input);
154
- }}
155
- isLoading={mutations.createState.loading}
156
- />
148
+ {/* Create Project Modal */}
149
+ <CreateProjectModal
150
+ isOpen={isCreateModalOpen}
151
+ onClose={() => setIsCreateModalOpen(false)}
152
+ onSubmit={async (input) => {
153
+ await mutations.createProject(input);
154
+ }}
155
+ isLoading={mutations.createState.loading}
156
+ />
157
157
 
158
- {/* Project Actions Modal */}
159
- <ProjectActionsModal
160
- isOpen={isProjectActionsOpen}
161
- project={selectedProject}
162
- onClose={() => {
163
- setIsProjectActionsOpen(false);
164
- setSelectedProject(null);
165
- }}
166
- onUpdate={async (input) => {
167
- await mutations.updateProject(input);
168
- }}
169
- onArchive={async (projectId) => {
170
- await mutations.archiveProject(projectId);
171
- }}
172
- onActivate={async (projectId) => {
173
- await mutations.activateProject(projectId);
174
- }}
175
- onDelete={async (projectId) => {
176
- await mutations.deleteProject(projectId);
177
- }}
178
- isLoading={mutations.isLoading}
179
- />
180
- </div>
181
- );
158
+ {/* Project Actions Modal */}
159
+ <ProjectActionsModal
160
+ isOpen={isProjectActionsOpen}
161
+ project={selectedProject}
162
+ onClose={() => {
163
+ setIsProjectActionsOpen(false);
164
+ setSelectedProject(null);
165
+ }}
166
+ onUpdate={async (input) => {
167
+ await mutations.updateProject(input);
168
+ }}
169
+ onArchive={async (projectId) => {
170
+ await mutations.archiveProject(projectId);
171
+ }}
172
+ onActivate={async (projectId) => {
173
+ await mutations.activateProject(projectId);
174
+ }}
175
+ onDelete={async (projectId) => {
176
+ await mutations.deleteProject(projectId);
177
+ }}
178
+ isLoading={mutations.isLoading}
179
+ />
180
+ </div>
181
+ );
182
182
  }
183
183
 
184
184
  interface ProjectsTabProps {
185
- data: ReturnType<typeof useProjectList>['data'];
186
- onProjectClick?: (project: Project) => void;
185
+ data: ReturnType<typeof useProjectList>['data'];
186
+ onProjectClick?: (project: Project) => void;
187
187
  }
188
188
 
189
189
  function ProjectsTab({ data, onProjectClick }: ProjectsTabProps) {
190
- if (!data?.items.length) {
191
- return (
192
- <EmptyState
193
- title="No projects yet"
194
- description="Create your first project to get started."
195
- />
196
- );
197
- }
190
+ if (!data?.items.length) {
191
+ return (
192
+ <EmptyState
193
+ title="No projects yet"
194
+ description="Create your first project to get started."
195
+ />
196
+ );
197
+ }
198
198
 
199
- return (
200
- <div className="space-y-4">
201
- <div className="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
202
- {data.items.map((project: Project) => (
203
- <EntityCard
204
- key={project.id}
205
- cardTitle={project.name}
206
- cardSubtitle={project.tier}
207
- meta={
208
- <p className="text-muted-foreground text-sm">
209
- {project.description}
210
- </p>
211
- }
212
- chips={
213
- <StatusChip
214
- tone={getStatusTone(project.status)}
215
- label={project.status}
216
- />
217
- }
218
- footer={
219
- <div className="flex w-full items-center justify-between">
220
- <span className="text-muted-foreground text-xs">
221
- {project.updatedAt.toLocaleDateString()}
222
- </span>
223
- <Button
224
- variant="ghost"
225
- size="sm"
226
- onPress={() => onProjectClick?.(project)}
227
- >
228
- Actions
229
- </Button>
230
- </div>
231
- }
232
- />
233
- ))}
234
- </div>
235
- </div>
236
- );
199
+ return (
200
+ <div className="space-y-4">
201
+ <div className="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
202
+ {data.items.map((project: Project) => (
203
+ <EntityCard
204
+ key={project.id}
205
+ cardTitle={project.name}
206
+ cardSubtitle={project.tier}
207
+ meta={
208
+ <p className="text-muted-foreground text-sm">
209
+ {project.description}
210
+ </p>
211
+ }
212
+ chips={
213
+ <StatusChip
214
+ tone={getStatusTone(project.status)}
215
+ label={project.status}
216
+ />
217
+ }
218
+ footer={
219
+ <div className="flex w-full items-center justify-between">
220
+ <span className="text-muted-foreground text-xs">
221
+ {project.updatedAt.toLocaleDateString()}
222
+ </span>
223
+ <Button
224
+ variant="ghost"
225
+ size="sm"
226
+ onPress={() => onProjectClick?.(project)}
227
+ >
228
+ Actions
229
+ </Button>
230
+ </div>
231
+ }
232
+ />
233
+ ))}
234
+ </div>
235
+ </div>
236
+ );
237
237
  }
238
238
 
239
239
  function BillingTab({ subscription }: { subscription: Subscription | null }) {
240
- if (!subscription) return null;
240
+ if (!subscription) return null;
241
241
 
242
- return (
243
- <div className="space-y-6">
244
- <div className="border-border bg-card rounded-xl border p-6">
245
- <div className="flex items-start justify-between">
246
- <div>
247
- <h3 className="text-lg font-semibold">{subscription.plan} Plan</h3>
248
- <p className="text-muted-foreground text-sm">
249
- Current period:{' '}
250
- {subscription.currentPeriodStart.toLocaleDateString()} -{' '}
251
- {subscription.currentPeriodEnd.toLocaleDateString()}
252
- </p>
253
- <p className="text-muted-foreground text-sm">
254
- Billing cycle: {subscription.billingCycle}
255
- </p>
256
- </div>
257
- <StatusChip tone="success" label={subscription.status} />
258
- </div>
242
+ return (
243
+ <div className="space-y-6">
244
+ <div className="rounded-xl border border-border bg-card p-6">
245
+ <div className="flex items-start justify-between">
246
+ <div>
247
+ <h3 className="font-semibold text-lg">{subscription.plan} Plan</h3>
248
+ <p className="text-muted-foreground text-sm">
249
+ Current period:{' '}
250
+ {subscription.currentPeriodStart.toLocaleDateString()} -{' '}
251
+ {subscription.currentPeriodEnd.toLocaleDateString()}
252
+ </p>
253
+ <p className="text-muted-foreground text-sm">
254
+ Billing cycle: {subscription.billingCycle}
255
+ </p>
256
+ </div>
257
+ <StatusChip tone="success" label={subscription.status} />
258
+ </div>
259
259
 
260
- <div className="mt-4 flex gap-3">
261
- <Button variant="outline" onPress={() => alert('Upgrade clicked!')}>
262
- Upgrade Plan
263
- </Button>
264
- <Button
265
- variant="ghost"
266
- onPress={() => alert('Manage Billing clicked!')}
267
- >
268
- Manage Billing
269
- </Button>
270
- </div>
271
- </div>
260
+ <div className="mt-4 flex gap-3">
261
+ <Button variant="outline" onPress={() => alert('Upgrade clicked!')}>
262
+ Upgrade Plan
263
+ </Button>
264
+ <Button
265
+ variant="ghost"
266
+ onPress={() => alert('Manage Billing clicked!')}
267
+ >
268
+ Manage Billing
269
+ </Button>
270
+ </div>
271
+ </div>
272
272
 
273
- {subscription.cancelAtPeriodEnd && (
274
- <div className="border-border bg-destructive/10 text-destructive rounded-xl border p-4">
275
- <p className="text-sm font-medium">
276
- ⚠️ Your subscription will be cancelled at the end of the current
277
- period.
278
- </p>
279
- </div>
280
- )}
281
- </div>
282
- );
273
+ {subscription.cancelAtPeriodEnd && (
274
+ <div className="rounded-xl border border-border bg-destructive/10 p-4 text-destructive">
275
+ <p className="font-medium text-sm">
276
+ ⚠️ Your subscription will be cancelled at the end of the current
277
+ period.
278
+ </p>
279
+ </div>
280
+ )}
281
+ </div>
282
+ );
283
283
  }
284
284
 
285
285
  function SettingsTab() {
286
- return (
287
- <div className="space-y-6">
288
- <div className="border-border bg-card rounded-xl border p-6">
289
- <h3 className="mb-4 text-lg font-semibold">Organization Settings</h3>
290
- <div className="space-y-4">
291
- <div>
292
- <label htmlFor="org-name" className="text-sm font-medium">
293
- Organization Name
294
- </label>
295
- <input
296
- id="org-name"
297
- type="text"
298
- defaultValue="Demo Organization"
299
- className="border-input bg-background mt-1 block w-full rounded-md border px-3 py-2"
300
- />
301
- </div>
302
- <div>
303
- <label htmlFor="timezone" className="text-sm font-medium">
304
- Default Timezone
305
- </label>
306
- <select
307
- id="timezone"
308
- className="border-input bg-background mt-1 block w-full rounded-md border px-3 py-2"
309
- >
310
- <option>UTC</option>
311
- <option>America/New_York</option>
312
- <option>Europe/London</option>
313
- <option>Asia/Tokyo</option>
314
- </select>
315
- </div>
316
- <div className="pt-2">
317
- <Button onPress={() => alert('Settings saved!')}>
318
- Save Settings
319
- </Button>
320
- </div>
321
- </div>
322
- </div>
323
- </div>
324
- );
286
+ return (
287
+ <div className="space-y-6">
288
+ <div className="rounded-xl border border-border bg-card p-6">
289
+ <h3 className="mb-4 font-semibold text-lg">Organization Settings</h3>
290
+ <div className="space-y-4">
291
+ <div>
292
+ <label htmlFor="org-name" className="font-medium text-sm">
293
+ Organization Name
294
+ </label>
295
+ <input
296
+ id="org-name"
297
+ type="text"
298
+ defaultValue="Demo Organization"
299
+ className="mt-1 block w-full rounded-md border border-input bg-background px-3 py-2"
300
+ />
301
+ </div>
302
+ <div>
303
+ <label htmlFor="timezone" className="font-medium text-sm">
304
+ Default Timezone
305
+ </label>
306
+ <select
307
+ id="timezone"
308
+ className="mt-1 block w-full rounded-md border border-input bg-background px-3 py-2"
309
+ >
310
+ <option>UTC</option>
311
+ <option>America/New_York</option>
312
+ <option>Europe/London</option>
313
+ <option>Asia/Tokyo</option>
314
+ </select>
315
+ </div>
316
+ <div className="pt-2">
317
+ <Button onPress={() => alert('Settings saved!')}>
318
+ Save Settings
319
+ </Button>
320
+ </div>
321
+ </div>
322
+ </div>
323
+ </div>
324
+ );
325
325
  }