@contractspec/example.saas-boilerplate 3.7.6 → 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 (114) hide show
  1. package/.turbo/turbo-build.log +8 -8
  2. package/AGENTS.md +50 -27
  3. package/README.md +64 -144
  4. package/dist/billing/billing.event.js +1 -1
  5. package/dist/billing/index.d.ts +6 -6
  6. package/dist/billing/index.js +1 -1
  7. package/dist/browser/billing/billing.event.js +1 -1
  8. package/dist/browser/billing/index.js +1 -1
  9. package/dist/browser/index.js +931 -932
  10. package/dist/browser/project/index.js +209 -209
  11. package/dist/browser/project/project.event.js +1 -1
  12. package/dist/browser/ui/SaasDashboard.js +45 -45
  13. package/dist/browser/ui/SaasProjectList.js +7 -7
  14. package/dist/browser/ui/SaasSettingsPanel.js +12 -12
  15. package/dist/browser/ui/hooks/index.js +2 -2
  16. package/dist/browser/ui/hooks/useProjectList.js +1 -1
  17. package/dist/browser/ui/hooks/useProjectMutations.js +1 -1
  18. package/dist/browser/ui/index.js +483 -484
  19. package/dist/browser/ui/modals/CreateProjectModal.js +10 -10
  20. package/dist/browser/ui/modals/ProjectActionsModal.js +13 -13
  21. package/dist/browser/ui/modals/index.js +23 -23
  22. package/dist/browser/ui/renderers/index.js +112 -112
  23. package/dist/browser/ui/renderers/project-list.renderer.js +7 -7
  24. package/dist/handlers/index.d.ts +2 -2
  25. package/dist/index.d.ts +4 -4
  26. package/dist/index.js +931 -932
  27. package/dist/node/billing/billing.event.js +1 -1
  28. package/dist/node/billing/index.js +1 -1
  29. package/dist/node/index.js +931 -932
  30. package/dist/node/project/index.js +209 -209
  31. package/dist/node/project/project.event.js +1 -1
  32. package/dist/node/ui/SaasDashboard.js +45 -45
  33. package/dist/node/ui/SaasProjectList.js +7 -7
  34. package/dist/node/ui/SaasSettingsPanel.js +12 -12
  35. package/dist/node/ui/hooks/index.js +2 -2
  36. package/dist/node/ui/hooks/useProjectList.js +1 -1
  37. package/dist/node/ui/hooks/useProjectMutations.js +1 -1
  38. package/dist/node/ui/index.js +483 -484
  39. package/dist/node/ui/modals/CreateProjectModal.js +10 -10
  40. package/dist/node/ui/modals/ProjectActionsModal.js +13 -13
  41. package/dist/node/ui/modals/index.js +23 -23
  42. package/dist/node/ui/renderers/index.js +112 -112
  43. package/dist/node/ui/renderers/project-list.renderer.js +7 -7
  44. package/dist/presentations/index.d.ts +1 -1
  45. package/dist/project/index.d.ts +7 -7
  46. package/dist/project/index.js +209 -209
  47. package/dist/project/project.event.js +1 -1
  48. package/dist/settings/index.d.ts +1 -1
  49. package/dist/ui/SaasDashboard.js +45 -45
  50. package/dist/ui/SaasProjectList.js +7 -7
  51. package/dist/ui/SaasSettingsPanel.js +12 -12
  52. package/dist/ui/hooks/index.d.ts +2 -2
  53. package/dist/ui/hooks/index.js +2 -2
  54. package/dist/ui/hooks/useProjectList.d.ts +5 -0
  55. package/dist/ui/hooks/useProjectList.js +1 -1
  56. package/dist/ui/hooks/useProjectMutations.d.ts +8 -0
  57. package/dist/ui/hooks/useProjectMutations.js +1 -1
  58. package/dist/ui/index.d.ts +4 -4
  59. package/dist/ui/index.js +483 -484
  60. package/dist/ui/modals/CreateProjectModal.js +10 -10
  61. package/dist/ui/modals/ProjectActionsModal.js +13 -13
  62. package/dist/ui/modals/index.js +23 -23
  63. package/dist/ui/renderers/index.d.ts +1 -1
  64. package/dist/ui/renderers/index.js +112 -112
  65. package/dist/ui/renderers/project-list.renderer.d.ts +1 -1
  66. package/dist/ui/renderers/project-list.renderer.js +7 -7
  67. package/package.json +10 -10
  68. package/src/billing/billing.entity.ts +132 -132
  69. package/src/billing/billing.enum.ts +9 -9
  70. package/src/billing/billing.event.ts +71 -71
  71. package/src/billing/billing.handler.ts +87 -87
  72. package/src/billing/billing.operations.ts +158 -158
  73. package/src/billing/billing.presentation.ts +45 -45
  74. package/src/billing/billing.schema.ts +76 -76
  75. package/src/billing/index.ts +43 -48
  76. package/src/dashboard/dashboard.presentation.ts +45 -45
  77. package/src/dashboard/index.ts +2 -2
  78. package/src/docs/saas-boilerplate.docblock.ts +43 -43
  79. package/src/example.ts +32 -32
  80. package/src/handlers/index.ts +9 -9
  81. package/src/handlers/saas.handlers.ts +250 -249
  82. package/src/index.ts +40 -41
  83. package/src/presentations/index.ts +18 -20
  84. package/src/project/index.ts +45 -50
  85. package/src/project/project.entity.ts +68 -68
  86. package/src/project/project.enum.ts +8 -8
  87. package/src/project/project.event.ts +79 -79
  88. package/src/project/project.handler.ts +103 -103
  89. package/src/project/project.operations.ts +236 -236
  90. package/src/project/project.presentation.ts +46 -46
  91. package/src/project/project.schema.ts +90 -90
  92. package/src/saas-boilerplate.feature.ts +100 -100
  93. package/src/seeders/index.ts +20 -20
  94. package/src/settings/index.ts +2 -3
  95. package/src/settings/settings.entity.ts +65 -65
  96. package/src/settings/settings.enum.ts +4 -4
  97. package/src/shared/mock-data.ts +92 -92
  98. package/src/shared/overlay-types.ts +23 -23
  99. package/src/tests/operations.test-spec.ts +96 -96
  100. package/src/ui/SaasDashboard.tsx +270 -270
  101. package/src/ui/SaasProjectList.tsx +90 -90
  102. package/src/ui/SaasSettingsPanel.tsx +84 -84
  103. package/src/ui/hooks/index.ts +3 -3
  104. package/src/ui/hooks/useProjectList.ts +69 -68
  105. package/src/ui/hooks/useProjectMutations.ts +144 -143
  106. package/src/ui/index.ts +8 -12
  107. package/src/ui/modals/CreateProjectModal.tsx +154 -154
  108. package/src/ui/modals/ProjectActionsModal.tsx +321 -321
  109. package/src/ui/overlays/demo-overlays.ts +49 -49
  110. package/src/ui/renderers/index.ts +5 -4
  111. package/src/ui/renderers/project-list.markdown.ts +204 -204
  112. package/src/ui/renderers/project-list.renderer.tsx +14 -13
  113. package/tsconfig.json +7 -8
  114. package/tsdown.config.js +7 -3
package/dist/index.js CHANGED
@@ -124,8 +124,8 @@ var FeatureAccessReasonEnum = defineEnum("FeatureAccessReason", [
124
124
  ]);
125
125
 
126
126
  // src/billing/billing.event.ts
127
- import { ScalarTypeEnum, defineSchemaModel } from "@contractspec/lib.schema";
128
127
  import { defineEvent } from "@contractspec/lib.contracts-spec";
128
+ import { defineSchemaModel, ScalarTypeEnum } from "@contractspec/lib.schema";
129
129
  var UsageRecordedPayload = defineSchemaModel({
130
130
  name: "UsageRecordedPayload",
131
131
  description: "Payload when feature usage is recorded",
@@ -1044,6 +1044,79 @@ function createSaasHandlers(db) {
1044
1044
  getSubscription
1045
1045
  };
1046
1046
  }
1047
+ // src/project/project.entity.ts
1048
+ import {
1049
+ defineEntity as defineEntity2,
1050
+ defineEntityEnum as defineEntityEnum2,
1051
+ field as field2,
1052
+ index as index2
1053
+ } from "@contractspec/lib.schema";
1054
+ var ProjectStatusEnum = defineEntityEnum2({
1055
+ name: "ProjectStatus",
1056
+ values: ["DRAFT", "ACTIVE", "ARCHIVED", "DELETED"],
1057
+ schema: "saas_app",
1058
+ description: "Status of a project."
1059
+ });
1060
+ var ProjectEntity = defineEntity2({
1061
+ name: "Project",
1062
+ description: "A project belonging to an organization.",
1063
+ schema: "saas_app",
1064
+ map: "project",
1065
+ fields: {
1066
+ id: field2.id({ description: "Unique project ID" }),
1067
+ name: field2.string({ description: "Project name" }),
1068
+ description: field2.string({
1069
+ isOptional: true,
1070
+ description: "Project description"
1071
+ }),
1072
+ slug: field2.string({
1073
+ isOptional: true,
1074
+ description: "URL-friendly identifier"
1075
+ }),
1076
+ organizationId: field2.foreignKey({ description: "Owning organization" }),
1077
+ createdBy: field2.foreignKey({
1078
+ description: "User who created the project"
1079
+ }),
1080
+ status: field2.enum("ProjectStatus", { default: "DRAFT" }),
1081
+ isPublic: field2.boolean({
1082
+ default: false,
1083
+ description: "Whether project is publicly visible"
1084
+ }),
1085
+ settings: field2.json({
1086
+ isOptional: true,
1087
+ description: "Project-specific settings"
1088
+ }),
1089
+ tags: field2.string({ isArray: true, description: "Project tags" }),
1090
+ metadata: field2.json({ isOptional: true }),
1091
+ createdAt: field2.createdAt(),
1092
+ updatedAt: field2.updatedAt(),
1093
+ archivedAt: field2.dateTime({ isOptional: true })
1094
+ },
1095
+ indexes: [
1096
+ index2.on(["organizationId", "status"]),
1097
+ index2.on(["organizationId", "createdAt"]),
1098
+ index2.unique(["organizationId", "slug"])
1099
+ ],
1100
+ enums: [ProjectStatusEnum]
1101
+ });
1102
+ var ProjectMemberEntity = defineEntity2({
1103
+ name: "ProjectMember",
1104
+ description: "User access to a specific project.",
1105
+ schema: "saas_app",
1106
+ map: "project_member",
1107
+ fields: {
1108
+ id: field2.id(),
1109
+ projectId: field2.foreignKey(),
1110
+ userId: field2.foreignKey(),
1111
+ role: field2.string({
1112
+ description: "Role in project (owner, editor, viewer)"
1113
+ }),
1114
+ addedBy: field2.string({ isOptional: true }),
1115
+ createdAt: field2.createdAt()
1116
+ },
1117
+ indexes: [index2.unique(["projectId", "userId"])]
1118
+ });
1119
+
1047
1120
  // src/project/project.enum.ts
1048
1121
  import { defineEnum as defineEnum2 } from "@contractspec/lib.schema";
1049
1122
  var ProjectStatusSchemaEnum = defineEnum2("ProjectStatus", [
@@ -1059,112 +1132,210 @@ var ProjectStatusFilterEnum = defineEnum2("ProjectStatusFilter", [
1059
1132
  "all"
1060
1133
  ]);
1061
1134
 
1062
- // src/project/project.schema.ts
1135
+ // src/project/project.event.ts
1136
+ import { defineEvent as defineEvent2 } from "@contractspec/lib.contracts-spec";
1063
1137
  import { defineSchemaModel as defineSchemaModel3, ScalarTypeEnum as ScalarTypeEnum3 } from "@contractspec/lib.schema";
1064
- var ProjectModel = defineSchemaModel3({
1065
- name: "Project",
1066
- description: "A project within an organization",
1138
+ var ProjectCreatedPayload = defineSchemaModel3({
1139
+ name: "ProjectCreatedPayload",
1140
+ description: "Payload when a project is created",
1067
1141
  fields: {
1068
- id: { type: ScalarTypeEnum3.String_unsecure(), isOptional: false },
1142
+ projectId: { type: ScalarTypeEnum3.String_unsecure(), isOptional: false },
1069
1143
  name: { type: ScalarTypeEnum3.String_unsecure(), isOptional: false },
1070
- description: { type: ScalarTypeEnum3.String_unsecure(), isOptional: true },
1071
- slug: { type: ScalarTypeEnum3.String_unsecure(), isOptional: true },
1072
1144
  organizationId: {
1073
1145
  type: ScalarTypeEnum3.String_unsecure(),
1074
1146
  isOptional: false
1075
1147
  },
1076
1148
  createdBy: { type: ScalarTypeEnum3.String_unsecure(), isOptional: false },
1077
- status: { type: ProjectStatusSchemaEnum, isOptional: false },
1078
- isPublic: { type: ScalarTypeEnum3.Boolean(), isOptional: false },
1079
- tags: {
1149
+ createdAt: { type: ScalarTypeEnum3.DateTime(), isOptional: false }
1150
+ }
1151
+ });
1152
+ var ProjectUpdatedPayload = defineSchemaModel3({
1153
+ name: "ProjectUpdatedPayload",
1154
+ description: "Payload when a project is updated",
1155
+ fields: {
1156
+ projectId: { type: ScalarTypeEnum3.String_unsecure(), isOptional: false },
1157
+ updatedFields: {
1080
1158
  type: ScalarTypeEnum3.String_unsecure(),
1081
1159
  isArray: true,
1082
1160
  isOptional: false
1083
1161
  },
1084
- createdAt: { type: ScalarTypeEnum3.DateTime(), isOptional: false },
1162
+ updatedBy: { type: ScalarTypeEnum3.String_unsecure(), isOptional: false },
1085
1163
  updatedAt: { type: ScalarTypeEnum3.DateTime(), isOptional: false }
1086
1164
  }
1087
1165
  });
1088
- var CreateProjectInputModel = defineSchemaModel3({
1166
+ var ProjectDeletedPayload = defineSchemaModel3({
1167
+ name: "ProjectDeletedPayload",
1168
+ description: "Payload when a project is deleted",
1169
+ fields: {
1170
+ projectId: { type: ScalarTypeEnum3.String_unsecure(), isOptional: false },
1171
+ organizationId: {
1172
+ type: ScalarTypeEnum3.String_unsecure(),
1173
+ isOptional: false
1174
+ },
1175
+ deletedBy: { type: ScalarTypeEnum3.String_unsecure(), isOptional: false },
1176
+ deletedAt: { type: ScalarTypeEnum3.DateTime(), isOptional: false }
1177
+ }
1178
+ });
1179
+ var ProjectArchivedPayload = defineSchemaModel3({
1180
+ name: "ProjectArchivedPayload",
1181
+ description: "Payload when a project is archived",
1182
+ fields: {
1183
+ projectId: { type: ScalarTypeEnum3.String_unsecure(), isOptional: false },
1184
+ archivedBy: { type: ScalarTypeEnum3.String_unsecure(), isOptional: false },
1185
+ archivedAt: { type: ScalarTypeEnum3.DateTime(), isOptional: false }
1186
+ }
1187
+ });
1188
+ var ProjectCreatedEvent = defineEvent2({
1189
+ meta: {
1190
+ key: "project.created",
1191
+ version: "1.0.0",
1192
+ description: "A new project has been created.",
1193
+ stability: "stable",
1194
+ owners: ["@saas-team"],
1195
+ tags: ["project", "created"]
1196
+ },
1197
+ payload: ProjectCreatedPayload
1198
+ });
1199
+ var ProjectUpdatedEvent = defineEvent2({
1200
+ meta: {
1201
+ key: "project.updated",
1202
+ version: "1.0.0",
1203
+ description: "A project has been updated.",
1204
+ stability: "stable",
1205
+ owners: ["@saas-team"],
1206
+ tags: ["project", "updated"]
1207
+ },
1208
+ payload: ProjectUpdatedPayload
1209
+ });
1210
+ var ProjectDeletedEvent = defineEvent2({
1211
+ meta: {
1212
+ key: "project.deleted",
1213
+ version: "1.0.0",
1214
+ description: "A project has been deleted.",
1215
+ stability: "stable",
1216
+ owners: ["@saas-team"],
1217
+ tags: ["project", "deleted"]
1218
+ },
1219
+ payload: ProjectDeletedPayload
1220
+ });
1221
+ var ProjectArchivedEvent = defineEvent2({
1222
+ meta: {
1223
+ key: "project.archived",
1224
+ version: "1.0.0",
1225
+ description: "A project has been archived.",
1226
+ stability: "stable",
1227
+ owners: ["@saas-team"],
1228
+ tags: ["project", "archived"]
1229
+ },
1230
+ payload: ProjectArchivedPayload
1231
+ });
1232
+
1233
+ // src/project/project.schema.ts
1234
+ import { defineSchemaModel as defineSchemaModel4, ScalarTypeEnum as ScalarTypeEnum4 } from "@contractspec/lib.schema";
1235
+ var ProjectModel = defineSchemaModel4({
1236
+ name: "Project",
1237
+ description: "A project within an organization",
1238
+ fields: {
1239
+ id: { type: ScalarTypeEnum4.String_unsecure(), isOptional: false },
1240
+ name: { type: ScalarTypeEnum4.String_unsecure(), isOptional: false },
1241
+ description: { type: ScalarTypeEnum4.String_unsecure(), isOptional: true },
1242
+ slug: { type: ScalarTypeEnum4.String_unsecure(), isOptional: true },
1243
+ organizationId: {
1244
+ type: ScalarTypeEnum4.String_unsecure(),
1245
+ isOptional: false
1246
+ },
1247
+ createdBy: { type: ScalarTypeEnum4.String_unsecure(), isOptional: false },
1248
+ status: { type: ProjectStatusSchemaEnum, isOptional: false },
1249
+ isPublic: { type: ScalarTypeEnum4.Boolean(), isOptional: false },
1250
+ tags: {
1251
+ type: ScalarTypeEnum4.String_unsecure(),
1252
+ isArray: true,
1253
+ isOptional: false
1254
+ },
1255
+ createdAt: { type: ScalarTypeEnum4.DateTime(), isOptional: false },
1256
+ updatedAt: { type: ScalarTypeEnum4.DateTime(), isOptional: false }
1257
+ }
1258
+ });
1259
+ var CreateProjectInputModel = defineSchemaModel4({
1089
1260
  name: "CreateProjectInput",
1090
1261
  description: "Input for creating a project",
1091
1262
  fields: {
1092
- name: { type: ScalarTypeEnum3.NonEmptyString(), isOptional: false },
1093
- description: { type: ScalarTypeEnum3.String_unsecure(), isOptional: true },
1094
- slug: { type: ScalarTypeEnum3.String_unsecure(), isOptional: true },
1095
- isPublic: { type: ScalarTypeEnum3.Boolean(), isOptional: true },
1263
+ name: { type: ScalarTypeEnum4.NonEmptyString(), isOptional: false },
1264
+ description: { type: ScalarTypeEnum4.String_unsecure(), isOptional: true },
1265
+ slug: { type: ScalarTypeEnum4.String_unsecure(), isOptional: true },
1266
+ isPublic: { type: ScalarTypeEnum4.Boolean(), isOptional: true },
1096
1267
  tags: {
1097
- type: ScalarTypeEnum3.String_unsecure(),
1268
+ type: ScalarTypeEnum4.String_unsecure(),
1098
1269
  isArray: true,
1099
1270
  isOptional: true
1100
1271
  }
1101
1272
  }
1102
1273
  });
1103
- var UpdateProjectInputModel = defineSchemaModel3({
1274
+ var UpdateProjectInputModel = defineSchemaModel4({
1104
1275
  name: "UpdateProjectInput",
1105
1276
  description: "Input for updating a project",
1106
1277
  fields: {
1107
- projectId: { type: ScalarTypeEnum3.String_unsecure(), isOptional: false },
1108
- name: { type: ScalarTypeEnum3.String_unsecure(), isOptional: true },
1109
- description: { type: ScalarTypeEnum3.String_unsecure(), isOptional: true },
1110
- slug: { type: ScalarTypeEnum3.String_unsecure(), isOptional: true },
1111
- isPublic: { type: ScalarTypeEnum3.Boolean(), isOptional: true },
1278
+ projectId: { type: ScalarTypeEnum4.String_unsecure(), isOptional: false },
1279
+ name: { type: ScalarTypeEnum4.String_unsecure(), isOptional: true },
1280
+ description: { type: ScalarTypeEnum4.String_unsecure(), isOptional: true },
1281
+ slug: { type: ScalarTypeEnum4.String_unsecure(), isOptional: true },
1282
+ isPublic: { type: ScalarTypeEnum4.Boolean(), isOptional: true },
1112
1283
  tags: {
1113
- type: ScalarTypeEnum3.String_unsecure(),
1284
+ type: ScalarTypeEnum4.String_unsecure(),
1114
1285
  isArray: true,
1115
1286
  isOptional: true
1116
1287
  },
1117
1288
  status: { type: ProjectStatusSchemaEnum, isOptional: true }
1118
1289
  }
1119
1290
  });
1120
- var GetProjectInputModel = defineSchemaModel3({
1291
+ var GetProjectInputModel = defineSchemaModel4({
1121
1292
  name: "GetProjectInput",
1122
1293
  fields: {
1123
- projectId: { type: ScalarTypeEnum3.String_unsecure(), isOptional: false }
1294
+ projectId: { type: ScalarTypeEnum4.String_unsecure(), isOptional: false }
1124
1295
  }
1125
1296
  });
1126
- var DeleteProjectInputModel = defineSchemaModel3({
1297
+ var DeleteProjectInputModel = defineSchemaModel4({
1127
1298
  name: "DeleteProjectInput",
1128
1299
  fields: {
1129
- projectId: { type: ScalarTypeEnum3.String_unsecure(), isOptional: false }
1300
+ projectId: { type: ScalarTypeEnum4.String_unsecure(), isOptional: false }
1130
1301
  }
1131
1302
  });
1132
- var DeleteProjectOutputModel = defineSchemaModel3({
1303
+ var DeleteProjectOutputModel = defineSchemaModel4({
1133
1304
  name: "DeleteProjectOutput",
1134
1305
  fields: {
1135
- success: { type: ScalarTypeEnum3.Boolean(), isOptional: false }
1306
+ success: { type: ScalarTypeEnum4.Boolean(), isOptional: false }
1136
1307
  }
1137
1308
  });
1138
- var ProjectDeletedPayloadModel = defineSchemaModel3({
1309
+ var ProjectDeletedPayloadModel = defineSchemaModel4({
1139
1310
  name: "ProjectDeletedPayload",
1140
1311
  fields: {
1141
- projectId: { type: ScalarTypeEnum3.String_unsecure(), isOptional: false }
1312
+ projectId: { type: ScalarTypeEnum4.String_unsecure(), isOptional: false }
1142
1313
  }
1143
1314
  });
1144
- var ListProjectsInputModel = defineSchemaModel3({
1315
+ var ListProjectsInputModel = defineSchemaModel4({
1145
1316
  name: "ListProjectsInput",
1146
1317
  description: "Input for listing projects",
1147
1318
  fields: {
1148
1319
  status: { type: ProjectStatusFilterEnum, isOptional: true },
1149
- search: { type: ScalarTypeEnum3.String_unsecure(), isOptional: true },
1320
+ search: { type: ScalarTypeEnum4.String_unsecure(), isOptional: true },
1150
1321
  limit: {
1151
- type: ScalarTypeEnum3.Int_unsecure(),
1322
+ type: ScalarTypeEnum4.Int_unsecure(),
1152
1323
  isOptional: true,
1153
1324
  defaultValue: 20
1154
1325
  },
1155
1326
  offset: {
1156
- type: ScalarTypeEnum3.Int_unsecure(),
1327
+ type: ScalarTypeEnum4.Int_unsecure(),
1157
1328
  isOptional: true,
1158
1329
  defaultValue: 0
1159
1330
  }
1160
1331
  }
1161
1332
  });
1162
- var ListProjectsOutputModel = defineSchemaModel3({
1333
+ var ListProjectsOutputModel = defineSchemaModel4({
1163
1334
  name: "ListProjectsOutput",
1164
1335
  description: "Output for listing projects",
1165
1336
  fields: {
1166
1337
  projects: { type: ProjectModel, isArray: true, isOptional: false },
1167
- total: { type: ScalarTypeEnum3.Int_unsecure(), isOptional: false }
1338
+ total: { type: ScalarTypeEnum4.Int_unsecure(), isOptional: false }
1168
1339
  }
1169
1340
  });
1170
1341
 
@@ -1410,209 +1581,38 @@ var ListProjectsContract = defineQuery2({
1410
1581
  }
1411
1582
  });
1412
1583
 
1413
- // src/project/project.event.ts
1414
- import { ScalarTypeEnum as ScalarTypeEnum4, defineSchemaModel as defineSchemaModel4 } from "@contractspec/lib.schema";
1415
- import { defineEvent as defineEvent2 } from "@contractspec/lib.contracts-spec";
1416
- var ProjectCreatedPayload = defineSchemaModel4({
1417
- name: "ProjectCreatedPayload",
1418
- description: "Payload when a project is created",
1419
- fields: {
1420
- projectId: { type: ScalarTypeEnum4.String_unsecure(), isOptional: false },
1421
- name: { type: ScalarTypeEnum4.String_unsecure(), isOptional: false },
1422
- organizationId: {
1423
- type: ScalarTypeEnum4.String_unsecure(),
1424
- isOptional: false
1425
- },
1426
- createdBy: { type: ScalarTypeEnum4.String_unsecure(), isOptional: false },
1427
- createdAt: { type: ScalarTypeEnum4.DateTime(), isOptional: false }
1428
- }
1429
- });
1430
- var ProjectUpdatedPayload = defineSchemaModel4({
1431
- name: "ProjectUpdatedPayload",
1432
- description: "Payload when a project is updated",
1433
- fields: {
1434
- projectId: { type: ScalarTypeEnum4.String_unsecure(), isOptional: false },
1435
- updatedFields: {
1436
- type: ScalarTypeEnum4.String_unsecure(),
1437
- isArray: true,
1438
- isOptional: false
1439
- },
1440
- updatedBy: { type: ScalarTypeEnum4.String_unsecure(), isOptional: false },
1441
- updatedAt: { type: ScalarTypeEnum4.DateTime(), isOptional: false }
1442
- }
1443
- });
1444
- var ProjectDeletedPayload = defineSchemaModel4({
1445
- name: "ProjectDeletedPayload",
1446
- description: "Payload when a project is deleted",
1447
- fields: {
1448
- projectId: { type: ScalarTypeEnum4.String_unsecure(), isOptional: false },
1449
- organizationId: {
1450
- type: ScalarTypeEnum4.String_unsecure(),
1451
- isOptional: false
1452
- },
1453
- deletedBy: { type: ScalarTypeEnum4.String_unsecure(), isOptional: false },
1454
- deletedAt: { type: ScalarTypeEnum4.DateTime(), isOptional: false }
1455
- }
1456
- });
1457
- var ProjectArchivedPayload = defineSchemaModel4({
1458
- name: "ProjectArchivedPayload",
1459
- description: "Payload when a project is archived",
1460
- fields: {
1461
- projectId: { type: ScalarTypeEnum4.String_unsecure(), isOptional: false },
1462
- archivedBy: { type: ScalarTypeEnum4.String_unsecure(), isOptional: false },
1463
- archivedAt: { type: ScalarTypeEnum4.DateTime(), isOptional: false }
1464
- }
1465
- });
1466
- var ProjectCreatedEvent = defineEvent2({
1584
+ // src/project/project.presentation.ts
1585
+ import {
1586
+ definePresentation as definePresentation3,
1587
+ StabilityEnum as StabilityEnum3
1588
+ } from "@contractspec/lib.contracts-spec";
1589
+ var ProjectListPresentation = definePresentation3({
1467
1590
  meta: {
1468
- key: "project.created",
1591
+ key: "saas.project.list",
1469
1592
  version: "1.0.0",
1470
- description: "A new project has been created.",
1471
- stability: "stable",
1593
+ title: "Project List",
1594
+ description: "List view of projects with status, tags, and last updated info",
1595
+ domain: "saas-boilerplate",
1472
1596
  owners: ["@saas-team"],
1473
- tags: ["project", "created"]
1597
+ tags: ["project", "list", "dashboard"],
1598
+ stability: StabilityEnum3.Beta,
1599
+ goal: "Browse and manage projects",
1600
+ context: "Project list page"
1474
1601
  },
1475
- payload: ProjectCreatedPayload
1602
+ source: {
1603
+ type: "component",
1604
+ framework: "react",
1605
+ componentKey: "ProjectListView",
1606
+ props: ProjectModel
1607
+ },
1608
+ targets: ["react", "markdown", "application/json"],
1609
+ policy: {
1610
+ flags: ["saas.projects.enabled"]
1611
+ }
1476
1612
  });
1477
- var ProjectUpdatedEvent = defineEvent2({
1613
+ var ProjectDetailPresentation = definePresentation3({
1478
1614
  meta: {
1479
- key: "project.updated",
1480
- version: "1.0.0",
1481
- description: "A project has been updated.",
1482
- stability: "stable",
1483
- owners: ["@saas-team"],
1484
- tags: ["project", "updated"]
1485
- },
1486
- payload: ProjectUpdatedPayload
1487
- });
1488
- var ProjectDeletedEvent = defineEvent2({
1489
- meta: {
1490
- key: "project.deleted",
1491
- version: "1.0.0",
1492
- description: "A project has been deleted.",
1493
- stability: "stable",
1494
- owners: ["@saas-team"],
1495
- tags: ["project", "deleted"]
1496
- },
1497
- payload: ProjectDeletedPayload
1498
- });
1499
- var ProjectArchivedEvent = defineEvent2({
1500
- meta: {
1501
- key: "project.archived",
1502
- version: "1.0.0",
1503
- description: "A project has been archived.",
1504
- stability: "stable",
1505
- owners: ["@saas-team"],
1506
- tags: ["project", "archived"]
1507
- },
1508
- payload: ProjectArchivedPayload
1509
- });
1510
-
1511
- // src/project/project.entity.ts
1512
- import {
1513
- defineEntity as defineEntity2,
1514
- defineEntityEnum as defineEntityEnum2,
1515
- field as field2,
1516
- index as index2
1517
- } from "@contractspec/lib.schema";
1518
- var ProjectStatusEnum = defineEntityEnum2({
1519
- name: "ProjectStatus",
1520
- values: ["DRAFT", "ACTIVE", "ARCHIVED", "DELETED"],
1521
- schema: "saas_app",
1522
- description: "Status of a project."
1523
- });
1524
- var ProjectEntity = defineEntity2({
1525
- name: "Project",
1526
- description: "A project belonging to an organization.",
1527
- schema: "saas_app",
1528
- map: "project",
1529
- fields: {
1530
- id: field2.id({ description: "Unique project ID" }),
1531
- name: field2.string({ description: "Project name" }),
1532
- description: field2.string({
1533
- isOptional: true,
1534
- description: "Project description"
1535
- }),
1536
- slug: field2.string({
1537
- isOptional: true,
1538
- description: "URL-friendly identifier"
1539
- }),
1540
- organizationId: field2.foreignKey({ description: "Owning organization" }),
1541
- createdBy: field2.foreignKey({
1542
- description: "User who created the project"
1543
- }),
1544
- status: field2.enum("ProjectStatus", { default: "DRAFT" }),
1545
- isPublic: field2.boolean({
1546
- default: false,
1547
- description: "Whether project is publicly visible"
1548
- }),
1549
- settings: field2.json({
1550
- isOptional: true,
1551
- description: "Project-specific settings"
1552
- }),
1553
- tags: field2.string({ isArray: true, description: "Project tags" }),
1554
- metadata: field2.json({ isOptional: true }),
1555
- createdAt: field2.createdAt(),
1556
- updatedAt: field2.updatedAt(),
1557
- archivedAt: field2.dateTime({ isOptional: true })
1558
- },
1559
- indexes: [
1560
- index2.on(["organizationId", "status"]),
1561
- index2.on(["organizationId", "createdAt"]),
1562
- index2.unique(["organizationId", "slug"])
1563
- ],
1564
- enums: [ProjectStatusEnum]
1565
- });
1566
- var ProjectMemberEntity = defineEntity2({
1567
- name: "ProjectMember",
1568
- description: "User access to a specific project.",
1569
- schema: "saas_app",
1570
- map: "project_member",
1571
- fields: {
1572
- id: field2.id(),
1573
- projectId: field2.foreignKey(),
1574
- userId: field2.foreignKey(),
1575
- role: field2.string({
1576
- description: "Role in project (owner, editor, viewer)"
1577
- }),
1578
- addedBy: field2.string({ isOptional: true }),
1579
- createdAt: field2.createdAt()
1580
- },
1581
- indexes: [index2.unique(["projectId", "userId"])]
1582
- });
1583
-
1584
- // src/project/project.presentation.ts
1585
- import {
1586
- definePresentation as definePresentation3,
1587
- StabilityEnum as StabilityEnum3
1588
- } from "@contractspec/lib.contracts-spec";
1589
- var ProjectListPresentation = definePresentation3({
1590
- meta: {
1591
- key: "saas.project.list",
1592
- version: "1.0.0",
1593
- title: "Project List",
1594
- description: "List view of projects with status, tags, and last updated info",
1595
- domain: "saas-boilerplate",
1596
- owners: ["@saas-team"],
1597
- tags: ["project", "list", "dashboard"],
1598
- stability: StabilityEnum3.Beta,
1599
- goal: "Browse and manage projects",
1600
- context: "Project list page"
1601
- },
1602
- source: {
1603
- type: "component",
1604
- framework: "react",
1605
- componentKey: "ProjectListView",
1606
- props: ProjectModel
1607
- },
1608
- targets: ["react", "markdown", "application/json"],
1609
- policy: {
1610
- flags: ["saas.projects.enabled"]
1611
- }
1612
- });
1613
- var ProjectDetailPresentation = definePresentation3({
1614
- meta: {
1615
- key: "saas.project.detail",
1615
+ key: "saas.project.detail",
1616
1616
  version: "1.0.0",
1617
1617
  title: "Project Details",
1618
1618
  description: "Detailed view of a project with settings and activity",
@@ -1633,76 +1633,6 @@ var ProjectDetailPresentation = definePresentation3({
1633
1633
  flags: ["saas.projects.enabled"]
1634
1634
  }
1635
1635
  });
1636
- // src/settings/settings.enum.ts
1637
- import { defineEntityEnum as defineEntityEnum3 } from "@contractspec/lib.schema";
1638
- var SettingsScopeEnum = defineEntityEnum3({
1639
- name: "SettingsScope",
1640
- values: ["APP", "ORG", "USER", "PROJECT"],
1641
- schema: "saas_app",
1642
- description: "Scope of a setting."
1643
- });
1644
-
1645
- // src/settings/settings.entity.ts
1646
- import { defineEntity as defineEntity3, field as field3, index as index3 } from "@contractspec/lib.schema";
1647
- var SettingsEntity = defineEntity3({
1648
- name: "Settings",
1649
- description: "Application, organization, or user settings.",
1650
- schema: "saas_app",
1651
- map: "settings",
1652
- fields: {
1653
- id: field3.id(),
1654
- key: field3.string({
1655
- description: 'Setting key (e.g., "theme", "notifications.email")'
1656
- }),
1657
- scope: field3.enum("SettingsScope"),
1658
- scopeId: field3.string({
1659
- isOptional: true,
1660
- description: "ID of scoped entity (org, user, project)"
1661
- }),
1662
- value: field3.json({ description: "Setting value" }),
1663
- valueType: field3.string({
1664
- default: '"string"',
1665
- description: "Type hint for value"
1666
- }),
1667
- schema: field3.json({
1668
- isOptional: true,
1669
- description: "JSON schema for validation"
1670
- }),
1671
- description: field3.string({ isOptional: true }),
1672
- isSecret: field3.boolean({
1673
- default: false,
1674
- description: "Whether value should be encrypted"
1675
- }),
1676
- createdAt: field3.createdAt(),
1677
- updatedAt: field3.updatedAt()
1678
- },
1679
- indexes: [
1680
- index3.unique(["scope", "scopeId", "key"]),
1681
- index3.on(["scope", "key"])
1682
- ],
1683
- enums: [SettingsScopeEnum]
1684
- });
1685
- var FeatureFlagEntity = defineEntity3({
1686
- name: "FeatureFlag",
1687
- description: "Feature flags for progressive rollout.",
1688
- schema: "saas_app",
1689
- map: "feature_flag",
1690
- fields: {
1691
- id: field3.id(),
1692
- key: field3.string({ isUnique: true, description: "Feature flag key" }),
1693
- name: field3.string({ description: "Human-readable name" }),
1694
- description: field3.string({ isOptional: true }),
1695
- enabled: field3.boolean({ default: false }),
1696
- defaultValue: field3.boolean({ default: false }),
1697
- rules: field3.json({ isOptional: true, description: "Targeting rules" }),
1698
- rolloutPercentage: field3.int({
1699
- default: 0,
1700
- description: "Percentage rollout (0-100)"
1701
- }),
1702
- createdAt: field3.createdAt(),
1703
- updatedAt: field3.updatedAt()
1704
- }
1705
- });
1706
1636
  // src/saas-boilerplate.feature.ts
1707
1637
  import { defineFeature } from "@contractspec/lib.contracts-spec";
1708
1638
  var SaasBoilerplateFeature = defineFeature({
@@ -1797,60 +1727,130 @@ var SaasBoilerplateFeature = defineFeature({
1797
1727
  ]
1798
1728
  });
1799
1729
 
1800
- // src/ui/hooks/useProjectList.ts
1801
- import { useCallback, useEffect, useMemo, useState } from "react";
1802
- import { useTemplateRuntime } from "@contractspec/lib.example-shared-ui";
1803
- function useProjectList(options = {}) {
1804
- const { handlers, projectId } = useTemplateRuntime();
1805
- const { saas: saas2 } = handlers;
1806
- const [data, setData] = useState(null);
1807
- const [subscription, setSubscription] = useState(null);
1808
- const [loading, setLoading] = useState(true);
1809
- const [error, setError] = useState(null);
1810
- const [page, setPage] = useState(1);
1811
- const fetchData = useCallback(async () => {
1812
- setLoading(true);
1813
- setError(null);
1814
- try {
1815
- const [projectsResult, subscriptionResult] = await Promise.all([
1816
- saas2.listProjects({
1817
- projectId,
1818
- status: options.status === "all" ? undefined : options.status,
1819
- search: options.search,
1820
- limit: options.limit ?? 20,
1821
- offset: (page - 1) * (options.limit ?? 20)
1822
- }),
1823
- saas2.getSubscription({ projectId })
1824
- ]);
1825
- setData({
1826
- items: projectsResult.items,
1827
- total: projectsResult.total
1828
- });
1829
- setSubscription(subscriptionResult);
1830
- } catch (err) {
1831
- setError(err instanceof Error ? err : new Error("Unknown error"));
1832
- } finally {
1833
- setLoading(false);
1834
- }
1835
- }, [saas2, projectId, options.status, options.search, options.limit, page]);
1836
- useEffect(() => {
1837
- fetchData();
1838
- }, [fetchData]);
1839
- const stats = useMemo(() => {
1840
- if (!data)
1841
- return null;
1842
- const items = data.items;
1843
- return {
1844
- total: data.total,
1845
- activeCount: items.filter((p) => p.status === "ACTIVE").length,
1846
- draftCount: items.filter((p) => p.status === "DRAFT").length,
1847
- projectLimit: 10,
1848
- usagePercent: Math.min(data.total / 10 * 100, 100)
1849
- };
1850
- }, [data]);
1851
- return {
1852
- data,
1853
- subscription,
1730
+ // src/settings/settings.enum.ts
1731
+ import { defineEntityEnum as defineEntityEnum3 } from "@contractspec/lib.schema";
1732
+ var SettingsScopeEnum = defineEntityEnum3({
1733
+ name: "SettingsScope",
1734
+ values: ["APP", "ORG", "USER", "PROJECT"],
1735
+ schema: "saas_app",
1736
+ description: "Scope of a setting."
1737
+ });
1738
+
1739
+ // src/settings/settings.entity.ts
1740
+ import { defineEntity as defineEntity3, field as field3, index as index3 } from "@contractspec/lib.schema";
1741
+ var SettingsEntity = defineEntity3({
1742
+ name: "Settings",
1743
+ description: "Application, organization, or user settings.",
1744
+ schema: "saas_app",
1745
+ map: "settings",
1746
+ fields: {
1747
+ id: field3.id(),
1748
+ key: field3.string({
1749
+ description: 'Setting key (e.g., "theme", "notifications.email")'
1750
+ }),
1751
+ scope: field3.enum("SettingsScope"),
1752
+ scopeId: field3.string({
1753
+ isOptional: true,
1754
+ description: "ID of scoped entity (org, user, project)"
1755
+ }),
1756
+ value: field3.json({ description: "Setting value" }),
1757
+ valueType: field3.string({
1758
+ default: '"string"',
1759
+ description: "Type hint for value"
1760
+ }),
1761
+ schema: field3.json({
1762
+ isOptional: true,
1763
+ description: "JSON schema for validation"
1764
+ }),
1765
+ description: field3.string({ isOptional: true }),
1766
+ isSecret: field3.boolean({
1767
+ default: false,
1768
+ description: "Whether value should be encrypted"
1769
+ }),
1770
+ createdAt: field3.createdAt(),
1771
+ updatedAt: field3.updatedAt()
1772
+ },
1773
+ indexes: [
1774
+ index3.unique(["scope", "scopeId", "key"]),
1775
+ index3.on(["scope", "key"])
1776
+ ],
1777
+ enums: [SettingsScopeEnum]
1778
+ });
1779
+ var FeatureFlagEntity = defineEntity3({
1780
+ name: "FeatureFlag",
1781
+ description: "Feature flags for progressive rollout.",
1782
+ schema: "saas_app",
1783
+ map: "feature_flag",
1784
+ fields: {
1785
+ id: field3.id(),
1786
+ key: field3.string({ isUnique: true, description: "Feature flag key" }),
1787
+ name: field3.string({ description: "Human-readable name" }),
1788
+ description: field3.string({ isOptional: true }),
1789
+ enabled: field3.boolean({ default: false }),
1790
+ defaultValue: field3.boolean({ default: false }),
1791
+ rules: field3.json({ isOptional: true, description: "Targeting rules" }),
1792
+ rolloutPercentage: field3.int({
1793
+ default: 0,
1794
+ description: "Percentage rollout (0-100)"
1795
+ }),
1796
+ createdAt: field3.createdAt(),
1797
+ updatedAt: field3.updatedAt()
1798
+ }
1799
+ });
1800
+ // src/ui/hooks/useProjectList.ts
1801
+ import { useTemplateRuntime } from "@contractspec/lib.example-shared-ui";
1802
+ import { useCallback, useEffect, useMemo, useState } from "react";
1803
+ function useProjectList(options = {}) {
1804
+ const { handlers, projectId } = useTemplateRuntime();
1805
+ const { saas: saas2 } = handlers;
1806
+ const [data, setData] = useState(null);
1807
+ const [subscription, setSubscription] = useState(null);
1808
+ const [loading, setLoading] = useState(true);
1809
+ const [error, setError] = useState(null);
1810
+ const [page, setPage] = useState(1);
1811
+ const fetchData = useCallback(async () => {
1812
+ setLoading(true);
1813
+ setError(null);
1814
+ try {
1815
+ const [projectsResult, subscriptionResult] = await Promise.all([
1816
+ saas2.listProjects({
1817
+ projectId,
1818
+ status: options.status === "all" ? undefined : options.status,
1819
+ search: options.search,
1820
+ limit: options.limit ?? 20,
1821
+ offset: (page - 1) * (options.limit ?? 20)
1822
+ }),
1823
+ saas2.getSubscription({ projectId })
1824
+ ]);
1825
+ setData({
1826
+ items: projectsResult.items,
1827
+ total: projectsResult.total
1828
+ });
1829
+ setSubscription(subscriptionResult);
1830
+ } catch (err) {
1831
+ setError(err instanceof Error ? err : new Error("Unknown error"));
1832
+ } finally {
1833
+ setLoading(false);
1834
+ }
1835
+ }, [saas2, projectId, options.status, options.search, options.limit, page]);
1836
+ useEffect(() => {
1837
+ fetchData();
1838
+ }, [fetchData]);
1839
+ const stats = useMemo(() => {
1840
+ if (!data)
1841
+ return null;
1842
+ const items = data.items;
1843
+ return {
1844
+ total: data.total,
1845
+ activeCount: items.filter((p) => p.status === "ACTIVE").length,
1846
+ draftCount: items.filter((p) => p.status === "DRAFT").length,
1847
+ projectLimit: 10,
1848
+ usagePercent: Math.min(data.total / 10 * 100, 100)
1849
+ };
1850
+ }, [data]);
1851
+ return {
1852
+ data,
1853
+ subscription,
1854
1854
  loading,
1855
1855
  error,
1856
1856
  stats,
@@ -1862,8 +1862,8 @@ function useProjectList(options = {}) {
1862
1862
  }
1863
1863
 
1864
1864
  // src/ui/hooks/useProjectMutations.ts
1865
- import { useCallback as useCallback2, useState as useState2 } from "react";
1866
1865
  import { useTemplateRuntime as useTemplateRuntime2 } from "@contractspec/lib.example-shared-ui";
1866
+ import { useCallback as useCallback2, useState as useState2 } from "react";
1867
1867
  function useProjectMutations(options = {}) {
1868
1868
  const { handlers, projectId } = useTemplateRuntime2();
1869
1869
  const { saas: saas2 } = handlers;
@@ -1950,9 +1950,12 @@ function useProjectMutations(options = {}) {
1950
1950
  };
1951
1951
  }
1952
1952
 
1953
+ // src/ui/hooks/index.ts
1954
+ "use client";
1955
+
1953
1956
  // src/ui/modals/CreateProjectModal.tsx
1954
- import { useState as useState3 } from "react";
1955
1957
  import { Button, Input } from "@contractspec/lib.design-system";
1958
+ import { useState as useState3 } from "react";
1956
1959
  import { jsxDEV } from "react/jsx-dev-runtime";
1957
1960
  "use client";
1958
1961
  var TIERS = [
@@ -1997,7 +2000,7 @@ function CreateProjectModal({
1997
2000
  className: "fixed inset-0 z-50 flex items-center justify-center",
1998
2001
  children: [
1999
2002
  /* @__PURE__ */ jsxDEV("div", {
2000
- className: "bg-background/80 absolute inset-0 backdrop-blur-sm",
2003
+ className: "absolute inset-0 bg-background/80 backdrop-blur-sm",
2001
2004
  onClick: onClose,
2002
2005
  role: "button",
2003
2006
  tabIndex: 0,
@@ -2008,10 +2011,10 @@ function CreateProjectModal({
2008
2011
  "aria-label": "Close modal"
2009
2012
  }, undefined, false, undefined, this),
2010
2013
  /* @__PURE__ */ jsxDEV("div", {
2011
- className: "bg-card border-border relative z-10 w-full max-w-md rounded-xl border p-6 shadow-xl",
2014
+ className: "relative z-10 w-full max-w-md rounded-xl border border-border bg-card p-6 shadow-xl",
2012
2015
  children: [
2013
2016
  /* @__PURE__ */ jsxDEV("h2", {
2014
- className: "mb-4 text-xl font-semibold",
2017
+ className: "mb-4 font-semibold text-xl",
2015
2018
  children: "Create New Project"
2016
2019
  }, undefined, false, undefined, this),
2017
2020
  /* @__PURE__ */ jsxDEV("form", {
@@ -2022,7 +2025,7 @@ function CreateProjectModal({
2022
2025
  children: [
2023
2026
  /* @__PURE__ */ jsxDEV("label", {
2024
2027
  htmlFor: "project-name",
2025
- className: "text-muted-foreground mb-1 block text-sm font-medium",
2028
+ className: "mb-1 block font-medium text-muted-foreground text-sm",
2026
2029
  children: "Project Name *"
2027
2030
  }, undefined, false, undefined, this),
2028
2031
  /* @__PURE__ */ jsxDEV(Input, {
@@ -2038,7 +2041,7 @@ function CreateProjectModal({
2038
2041
  children: [
2039
2042
  /* @__PURE__ */ jsxDEV("label", {
2040
2043
  htmlFor: "project-description",
2041
- className: "text-muted-foreground mb-1 block text-sm font-medium",
2044
+ className: "mb-1 block font-medium text-muted-foreground text-sm",
2042
2045
  children: "Description"
2043
2046
  }, undefined, false, undefined, this),
2044
2047
  /* @__PURE__ */ jsxDEV("textarea", {
@@ -2048,7 +2051,7 @@ function CreateProjectModal({
2048
2051
  placeholder: "Describe what this project is about...",
2049
2052
  rows: 3,
2050
2053
  disabled: isLoading,
2051
- className: "border-input bg-background focus:ring-ring w-full rounded-md border px-3 py-2 text-sm focus:ring-2 focus:outline-none disabled:opacity-50"
2054
+ className: "w-full rounded-md border border-input bg-background px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-ring disabled:opacity-50"
2052
2055
  }, undefined, false, undefined, this)
2053
2056
  ]
2054
2057
  }, undefined, true, undefined, this),
@@ -2056,7 +2059,7 @@ function CreateProjectModal({
2056
2059
  children: [
2057
2060
  /* @__PURE__ */ jsxDEV("label", {
2058
2061
  htmlFor: "project-tier",
2059
- className: "text-muted-foreground mb-1 block text-sm font-medium",
2062
+ className: "mb-1 block font-medium text-muted-foreground text-sm",
2060
2063
  children: "Tier"
2061
2064
  }, undefined, false, undefined, this),
2062
2065
  /* @__PURE__ */ jsxDEV("select", {
@@ -2064,7 +2067,7 @@ function CreateProjectModal({
2064
2067
  value: tier,
2065
2068
  onChange: (e) => setTier(e.target.value),
2066
2069
  disabled: isLoading,
2067
- className: "border-input bg-background focus:ring-ring h-10 w-full rounded-md border px-3 py-2 text-sm focus:ring-2 focus:outline-none disabled:opacity-50",
2070
+ className: "h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-ring disabled:opacity-50",
2068
2071
  children: TIERS.map((t) => /* @__PURE__ */ jsxDEV("option", {
2069
2072
  value: t.value,
2070
2073
  children: t.label
@@ -2073,7 +2076,7 @@ function CreateProjectModal({
2073
2076
  ]
2074
2077
  }, undefined, true, undefined, this),
2075
2078
  error && /* @__PURE__ */ jsxDEV("div", {
2076
- className: "bg-destructive/10 text-destructive rounded-md p-3 text-sm",
2079
+ className: "rounded-md bg-destructive/10 p-3 text-destructive text-sm",
2077
2080
  children: error
2078
2081
  }, undefined, false, undefined, this),
2079
2082
  /* @__PURE__ */ jsxDEV("div", {
@@ -2102,8 +2105,8 @@ function CreateProjectModal({
2102
2105
  }
2103
2106
 
2104
2107
  // src/ui/modals/ProjectActionsModal.tsx
2105
- import { useEffect as useEffect2, useState as useState4 } from "react";
2106
2108
  import { Button as Button2, Input as Input2 } from "@contractspec/lib.design-system";
2109
+ import { useEffect as useEffect2, useState as useState4 } from "react";
2107
2110
  import { jsxDEV as jsxDEV2 } from "react/jsx-dev-runtime";
2108
2111
  "use client";
2109
2112
  function ProjectActionsModal({
@@ -2196,7 +2199,7 @@ function ProjectActionsModal({
2196
2199
  className: "fixed inset-0 z-50 flex items-center justify-center",
2197
2200
  children: [
2198
2201
  /* @__PURE__ */ jsxDEV2("div", {
2199
- className: "bg-background/80 absolute inset-0 backdrop-blur-sm",
2202
+ className: "absolute inset-0 bg-background/80 backdrop-blur-sm",
2200
2203
  onClick: handleClose,
2201
2204
  role: "button",
2202
2205
  tabIndex: 0,
@@ -2207,13 +2210,13 @@ function ProjectActionsModal({
2207
2210
  "aria-label": "Close modal"
2208
2211
  }, undefined, false, undefined, this),
2209
2212
  /* @__PURE__ */ jsxDEV2("div", {
2210
- className: "bg-card border-border relative z-10 w-full max-w-md rounded-xl border p-6 shadow-xl",
2213
+ className: "relative z-10 w-full max-w-md rounded-xl border border-border bg-card p-6 shadow-xl",
2211
2214
  children: [
2212
2215
  /* @__PURE__ */ jsxDEV2("div", {
2213
- className: "border-border mb-4 border-b pb-4",
2216
+ className: "mb-4 border-border border-b pb-4",
2214
2217
  children: [
2215
2218
  /* @__PURE__ */ jsxDEV2("h2", {
2216
- className: "text-xl font-semibold",
2219
+ className: "font-semibold text-xl",
2217
2220
  children: project.name
2218
2221
  }, undefined, false, undefined, this),
2219
2222
  /* @__PURE__ */ jsxDEV2("p", {
@@ -2295,7 +2298,7 @@ function ProjectActionsModal({
2295
2298
  children: [
2296
2299
  /* @__PURE__ */ jsxDEV2("label", {
2297
2300
  htmlFor: "edit-name",
2298
- className: "text-muted-foreground mb-1 block text-sm font-medium",
2301
+ className: "mb-1 block font-medium text-muted-foreground text-sm",
2299
2302
  children: "Project Name *"
2300
2303
  }, undefined, false, undefined, this),
2301
2304
  /* @__PURE__ */ jsxDEV2(Input2, {
@@ -2310,7 +2313,7 @@ function ProjectActionsModal({
2310
2313
  children: [
2311
2314
  /* @__PURE__ */ jsxDEV2("label", {
2312
2315
  htmlFor: "edit-description",
2313
- className: "text-muted-foreground mb-1 block text-sm font-medium",
2316
+ className: "mb-1 block font-medium text-muted-foreground text-sm",
2314
2317
  children: "Description"
2315
2318
  }, undefined, false, undefined, this),
2316
2319
  /* @__PURE__ */ jsxDEV2("textarea", {
@@ -2319,12 +2322,12 @@ function ProjectActionsModal({
2319
2322
  onChange: (e) => setDescription(e.target.value),
2320
2323
  rows: 3,
2321
2324
  disabled: isLoading,
2322
- className: "border-input bg-background focus:ring-ring w-full rounded-md border px-3 py-2 text-sm focus:ring-2 focus:outline-none disabled:opacity-50"
2325
+ className: "w-full rounded-md border border-input bg-background px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-ring disabled:opacity-50"
2323
2326
  }, undefined, false, undefined, this)
2324
2327
  ]
2325
2328
  }, undefined, true, undefined, this),
2326
2329
  error && /* @__PURE__ */ jsxDEV2("div", {
2327
- className: "bg-destructive/10 text-destructive rounded-md p-3 text-sm",
2330
+ className: "rounded-md bg-destructive/10 p-3 text-destructive text-sm",
2328
2331
  children: error
2329
2332
  }, undefined, false, undefined, this),
2330
2333
  /* @__PURE__ */ jsxDEV2("div", {
@@ -2354,7 +2357,7 @@ function ProjectActionsModal({
2354
2357
  "Are you sure you want to archive",
2355
2358
  " ",
2356
2359
  /* @__PURE__ */ jsxDEV2("span", {
2357
- className: "text-foreground font-medium",
2360
+ className: "font-medium text-foreground",
2358
2361
  children: project.name
2359
2362
  }, undefined, false, undefined, this),
2360
2363
  "?"
@@ -2365,7 +2368,7 @@ function ProjectActionsModal({
2365
2368
  children: "Archived projects can be restored later."
2366
2369
  }, undefined, false, undefined, this),
2367
2370
  error && /* @__PURE__ */ jsxDEV2("div", {
2368
- className: "bg-destructive/10 text-destructive rounded-md p-3 text-sm",
2371
+ className: "rounded-md bg-destructive/10 p-3 text-destructive text-sm",
2369
2372
  children: error
2370
2373
  }, undefined, false, undefined, this),
2371
2374
  /* @__PURE__ */ jsxDEV2("div", {
@@ -2395,7 +2398,7 @@ function ProjectActionsModal({
2395
2398
  "Are you sure you want to delete",
2396
2399
  " ",
2397
2400
  /* @__PURE__ */ jsxDEV2("span", {
2398
- className: "text-foreground font-medium",
2401
+ className: "font-medium text-foreground",
2399
2402
  children: project.name
2400
2403
  }, undefined, false, undefined, this),
2401
2404
  "?"
@@ -2406,7 +2409,7 @@ function ProjectActionsModal({
2406
2409
  children: "This action cannot be undone."
2407
2410
  }, undefined, false, undefined, this),
2408
2411
  error && /* @__PURE__ */ jsxDEV2("div", {
2409
- className: "bg-destructive/10 text-destructive rounded-md p-3 text-sm",
2412
+ className: "rounded-md bg-destructive/10 p-3 text-destructive text-sm",
2410
2413
  children: error
2411
2414
  }, undefined, false, undefined, this),
2412
2415
  /* @__PURE__ */ jsxDEV2("div", {
@@ -2433,141 +2436,455 @@ function ProjectActionsModal({
2433
2436
  ]
2434
2437
  }, undefined, true, undefined, this);
2435
2438
  }
2436
-
2437
- // src/ui/SaasDashboard.tsx
2438
- import { useState as useState5, useCallback as useCallback3 } from "react";
2439
- import {
2440
- StatCard,
2441
- StatCardGroup,
2442
- StatusChip,
2443
- EntityCard,
2444
- EmptyState,
2445
- LoaderBlock,
2446
- ErrorState,
2447
- Button as Button3
2448
- } from "@contractspec/lib.design-system";
2449
- import { jsxDEV as jsxDEV3 } from "react/jsx-dev-runtime";
2450
- "use client";
2451
- function getStatusTone(status) {
2452
- switch (status) {
2453
- case "ACTIVE":
2454
- return "success";
2455
- case "DRAFT":
2456
- return "neutral";
2457
- case "ARCHIVED":
2458
- return "warning";
2459
- default:
2460
- return "neutral";
2461
- }
2462
- }
2463
- function SaasDashboard() {
2464
- const [activeTab, setActiveTab] = useState5("projects");
2465
- const [isCreateModalOpen, setIsCreateModalOpen] = useState5(false);
2466
- const [selectedProject, setSelectedProject] = useState5(null);
2467
- const [isProjectActionsOpen, setIsProjectActionsOpen] = useState5(false);
2468
- const { data, subscription, loading, error, stats, refetch } = useProjectList();
2469
- const mutations = useProjectMutations({
2470
- onSuccess: () => {
2471
- refetch();
2439
+ // src/ui/overlays/demo-overlays.ts
2440
+ var saasFreeUserOverlay = {
2441
+ overlayId: "saas-boilerplate.free-tier",
2442
+ version: "1.0.0",
2443
+ description: "Shows limitations for free tier users",
2444
+ appliesTo: {
2445
+ feature: "saas-boilerplate",
2446
+ tier: "free"
2447
+ },
2448
+ modifications: [
2449
+ {
2450
+ type: "setLimit",
2451
+ field: "projects",
2452
+ max: 3,
2453
+ message: "Upgrade to create more projects"
2454
+ },
2455
+ { type: "hideField", field: "advancedSettings", reason: "Pro feature" },
2456
+ {
2457
+ type: "addBadge",
2458
+ position: "header",
2459
+ label: "Free Plan",
2460
+ variant: "default"
2472
2461
  }
2473
- });
2474
- const handleProjectClick = useCallback3((project) => {
2475
- setSelectedProject(project);
2476
- setIsProjectActionsOpen(true);
2477
- }, []);
2478
- const tabs = [
2479
- { id: "projects", label: "Projects", icon: "\uD83D\uDCC1" },
2480
- { id: "billing", label: "Billing", icon: "\uD83D\uDCB3" },
2481
- { id: "settings", label: "Settings", icon: "\u2699\uFE0F" }
2482
- ];
2483
- if (loading && !data) {
2484
- return /* @__PURE__ */ jsxDEV3(LoaderBlock, {
2485
- label: "Loading dashboard..."
2486
- }, undefined, false, undefined, this);
2487
- }
2488
- if (error) {
2489
- return /* @__PURE__ */ jsxDEV3(ErrorState, {
2490
- title: "Failed to load dashboard",
2491
- description: error.message,
2492
- onRetry: refetch,
2493
- retryLabel: "Retry"
2494
- }, undefined, false, undefined, this);
2495
- }
2496
- return /* @__PURE__ */ jsxDEV3("div", {
2497
- className: "space-y-6",
2498
- children: [
2499
- /* @__PURE__ */ jsxDEV3("div", {
2500
- className: "flex items-center justify-between",
2501
- children: [
2502
- /* @__PURE__ */ jsxDEV3("h2", {
2503
- className: "text-2xl font-bold",
2504
- children: "SaaS Dashboard"
2505
- }, undefined, false, undefined, this),
2506
- activeTab === "projects" && /* @__PURE__ */ jsxDEV3(Button3, {
2507
- onPress: () => setIsCreateModalOpen(true),
2508
- children: [
2509
- /* @__PURE__ */ jsxDEV3("span", {
2510
- className: "mr-2",
2511
- children: "+"
2512
- }, undefined, false, undefined, this),
2513
- " New Project"
2514
- ]
2515
- }, undefined, true, undefined, this)
2516
- ]
2517
- }, undefined, true, undefined, this),
2518
- stats && subscription && /* @__PURE__ */ jsxDEV3(StatCardGroup, {
2519
- children: [
2520
- /* @__PURE__ */ jsxDEV3(StatCard, {
2521
- label: "Projects",
2522
- value: stats.total.toString()
2523
- }, undefined, false, undefined, this),
2524
- /* @__PURE__ */ jsxDEV3(StatCard, {
2525
- label: "Active",
2526
- value: stats.activeCount.toString()
2527
- }, undefined, false, undefined, this),
2528
- /* @__PURE__ */ jsxDEV3(StatCard, {
2529
- label: "Draft",
2530
- value: stats.draftCount.toString()
2531
- }, undefined, false, undefined, this),
2532
- /* @__PURE__ */ jsxDEV3(StatCard, {
2533
- label: "Plan",
2534
- value: subscription.plan,
2535
- hint: subscription.status
2536
- }, undefined, false, undefined, this)
2537
- ]
2538
- }, undefined, true, undefined, this),
2539
- /* @__PURE__ */ jsxDEV3("nav", {
2540
- className: "bg-muted flex gap-1 rounded-lg p-1",
2541
- role: "tablist",
2542
- children: tabs.map((tab) => /* @__PURE__ */ jsxDEV3("button", {
2543
- type: "button",
2544
- role: "tab",
2545
- "aria-selected": activeTab === tab.id,
2546
- onClick: () => setActiveTab(tab.id),
2547
- className: `flex flex-1 items-center justify-center gap-2 rounded-md px-4 py-2 text-sm font-medium transition-colors ${activeTab === tab.id ? "bg-background text-foreground shadow-sm" : "text-muted-foreground hover:text-foreground"}`,
2548
- children: [
2549
- /* @__PURE__ */ jsxDEV3("span", {
2550
- children: tab.icon
2462
+ ]
2463
+ };
2464
+ var saasDemoOverlay = {
2465
+ overlayId: "saas-boilerplate.demo-user",
2466
+ version: "1.0.0",
2467
+ description: "Demo mode for SaaS boilerplate",
2468
+ appliesTo: {
2469
+ feature: "saas-boilerplate",
2470
+ role: "demo"
2471
+ },
2472
+ modifications: [
2473
+ {
2474
+ type: "hideField",
2475
+ field: "billingSection",
2476
+ reason: "Demo users cannot access billing"
2477
+ },
2478
+ {
2479
+ type: "hideField",
2480
+ field: "deleteAccount",
2481
+ reason: "Not available in demo"
2482
+ },
2483
+ {
2484
+ type: "addBadge",
2485
+ position: "header",
2486
+ label: "Demo Mode",
2487
+ variant: "warning"
2488
+ }
2489
+ ]
2490
+ };
2491
+ var saasOverlays = [
2492
+ saasFreeUserOverlay,
2493
+ saasDemoOverlay
2494
+ ];
2495
+ // src/ui/renderers/project-list.markdown.ts
2496
+ var projectListMarkdownRenderer = {
2497
+ target: "markdown",
2498
+ render: async (desc, _ctx) => {
2499
+ if (desc.source.type !== "component" || desc.source.componentKey !== "ProjectListView") {
2500
+ throw new Error("projectListMarkdownRenderer: not ProjectListView");
2501
+ }
2502
+ const data = await mockListProjectsHandler({
2503
+ limit: 20,
2504
+ offset: 0
2505
+ });
2506
+ const items = data.projects ?? data.items ?? [];
2507
+ const lines = [
2508
+ "# Projects",
2509
+ "",
2510
+ `**Total**: ${data.total} projects`,
2511
+ ""
2512
+ ];
2513
+ if (items.length === 0) {
2514
+ lines.push("_No projects found._");
2515
+ } else {
2516
+ lines.push("| Status | Project | Description |");
2517
+ lines.push("|--------|---------|-------------|");
2518
+ for (const project of items) {
2519
+ const status = project.status === "ACTIVE" ? "\u2705" : project.status === "ARCHIVED" ? "\uD83D\uDCE6" : "\u23F8\uFE0F";
2520
+ lines.push(`| ${status} | **${project.name}** | ${project.description ?? "-"} |`);
2521
+ }
2522
+ }
2523
+ return {
2524
+ mimeType: "text/markdown",
2525
+ body: lines.join(`
2526
+ `)
2527
+ };
2528
+ }
2529
+ };
2530
+ var saasDashboardMarkdownRenderer = {
2531
+ target: "markdown",
2532
+ render: async (desc, _ctx) => {
2533
+ if (desc.source.type !== "component" || desc.source.componentKey !== "SaasDashboard") {
2534
+ throw new Error("saasDashboardMarkdownRenderer: not SaasDashboard");
2535
+ }
2536
+ const [projectsData, subscription] = await Promise.all([
2537
+ mockListProjectsHandler({ limit: 50 }),
2538
+ mockGetSubscriptionHandler()
2539
+ ]);
2540
+ const projects = projectsData.projects ?? [];
2541
+ const activeProjects = projects.filter((p) => p.status === "ACTIVE").length;
2542
+ const archivedProjects = projects.filter((p) => p.status === "ARCHIVED").length;
2543
+ const lines = [
2544
+ "# SaaS Dashboard",
2545
+ "",
2546
+ "> Organization overview and usage summary",
2547
+ "",
2548
+ "## Summary",
2549
+ "",
2550
+ "| Metric | Value |",
2551
+ "|--------|-------|",
2552
+ `| Total Projects | ${projectsData.total} |`,
2553
+ `| Active Projects | ${activeProjects} |`,
2554
+ `| Archived Projects | ${archivedProjects} |`,
2555
+ `| Subscription Plan | ${subscription.planName} |`,
2556
+ `| Subscription Status | ${subscription.status} |`,
2557
+ "",
2558
+ "## Projects",
2559
+ ""
2560
+ ];
2561
+ if (projects.length === 0) {
2562
+ lines.push("_No projects yet._");
2563
+ } else {
2564
+ lines.push("| Status | Project | Description |");
2565
+ lines.push("|--------|---------|-------------|");
2566
+ for (const project of projects.slice(0, 10)) {
2567
+ const status = project.status === "ACTIVE" ? "\u2705" : project.status === "ARCHIVED" ? "\uD83D\uDCE6" : "\u23F8\uFE0F";
2568
+ lines.push(`| ${status} | **${project.name}** | ${project.description ?? "-"} |`);
2569
+ }
2570
+ if (projects.length > 10) {
2571
+ lines.push(`| ... | ... | _${projectsData.total - 10} more projects_ |`);
2572
+ }
2573
+ }
2574
+ lines.push("");
2575
+ lines.push("## Subscription");
2576
+ lines.push("");
2577
+ lines.push(`- **Plan**: ${subscription.planName}`);
2578
+ lines.push(`- **Status**: ${subscription.status}`);
2579
+ if (subscription.currentPeriodEnd) {
2580
+ lines.push(`- **Period End**: ${new Date(subscription.currentPeriodEnd).toLocaleDateString()}`);
2581
+ }
2582
+ return {
2583
+ mimeType: "text/markdown",
2584
+ body: lines.join(`
2585
+ `)
2586
+ };
2587
+ }
2588
+ };
2589
+ var saasBillingMarkdownRenderer = {
2590
+ target: "markdown",
2591
+ render: async (desc, _ctx) => {
2592
+ if (desc.source.type !== "component" || desc.source.componentKey !== "SubscriptionView") {
2593
+ throw new Error("saasBillingMarkdownRenderer: not SubscriptionView");
2594
+ }
2595
+ const subscription = await mockGetSubscriptionHandler();
2596
+ const lines = [
2597
+ "# Billing & Subscription",
2598
+ "",
2599
+ "> Current subscription details and billing information",
2600
+ "",
2601
+ "## Subscription Details",
2602
+ "",
2603
+ "| Property | Value |",
2604
+ "|----------|-------|",
2605
+ `| Plan | ${subscription.planName} |`,
2606
+ `| Status | ${subscription.status} |`,
2607
+ `| ID | ${subscription.id} |`,
2608
+ `| Period Start | ${new Date(subscription.currentPeriodStart).toLocaleDateString()} |`,
2609
+ `| Period End | ${new Date(subscription.currentPeriodEnd).toLocaleDateString()} |`
2610
+ ];
2611
+ lines.push("");
2612
+ lines.push("## Plan Limits");
2613
+ lines.push("");
2614
+ lines.push(`- **Projects**: ${subscription.limits.projects}`);
2615
+ lines.push(`- **Users**: ${subscription.limits.users}`);
2616
+ lines.push("");
2617
+ lines.push("## Plan Features");
2618
+ lines.push("");
2619
+ if (subscription.planName.toLowerCase().includes("free")) {
2620
+ lines.push("- \u2705 Up to 3 projects");
2621
+ lines.push("- \u2705 Basic support");
2622
+ lines.push("- \u274C Priority support");
2623
+ lines.push("- \u274C Advanced analytics");
2624
+ } else if (subscription.planName.toLowerCase().includes("pro")) {
2625
+ lines.push("- \u2705 Unlimited projects");
2626
+ lines.push("- \u2705 Priority support");
2627
+ lines.push("- \u2705 Advanced analytics");
2628
+ lines.push("- \u274C Custom integrations");
2629
+ } else {
2630
+ lines.push("- \u2705 Unlimited projects");
2631
+ lines.push("- \u2705 Priority support");
2632
+ lines.push("- \u2705 Advanced analytics");
2633
+ lines.push("- \u2705 Custom integrations");
2634
+ lines.push("- \u2705 Dedicated support");
2635
+ }
2636
+ return {
2637
+ mimeType: "text/markdown",
2638
+ body: lines.join(`
2639
+ `)
2640
+ };
2641
+ }
2642
+ };
2643
+
2644
+ // src/ui/SaasProjectList.tsx
2645
+ import {
2646
+ Button as Button3,
2647
+ EmptyState,
2648
+ EntityCard,
2649
+ ErrorState,
2650
+ LoaderBlock,
2651
+ StatCard,
2652
+ StatCardGroup,
2653
+ StatusChip
2654
+ } from "@contractspec/lib.design-system";
2655
+ import { jsxDEV as jsxDEV3 } from "react/jsx-dev-runtime";
2656
+ "use client";
2657
+ function getStatusTone(status) {
2658
+ switch (status) {
2659
+ case "ACTIVE":
2660
+ return "success";
2661
+ case "DRAFT":
2662
+ return "neutral";
2663
+ case "ARCHIVED":
2664
+ return "danger";
2665
+ default:
2666
+ return "neutral";
2667
+ }
2668
+ }
2669
+ function SaasProjectList({
2670
+ onProjectClick,
2671
+ onCreateProject
2672
+ }) {
2673
+ const { data, loading, error, stats, refetch } = useProjectList();
2674
+ if (loading && !data) {
2675
+ return /* @__PURE__ */ jsxDEV3(LoaderBlock, {
2676
+ label: "Loading projects..."
2677
+ }, undefined, false, undefined, this);
2678
+ }
2679
+ if (error) {
2680
+ return /* @__PURE__ */ jsxDEV3(ErrorState, {
2681
+ title: "Failed to load projects",
2682
+ description: error.message,
2683
+ onRetry: refetch,
2684
+ retryLabel: "Retry"
2685
+ }, undefined, false, undefined, this);
2686
+ }
2687
+ if (!data?.items.length) {
2688
+ return /* @__PURE__ */ jsxDEV3(EmptyState, {
2689
+ title: "No projects found",
2690
+ description: "Create your first project to get started.",
2691
+ primaryAction: onCreateProject ? /* @__PURE__ */ jsxDEV3(Button3, {
2692
+ onPress: onCreateProject,
2693
+ children: "Create Project"
2694
+ }, undefined, false, undefined, this) : undefined
2695
+ }, undefined, false, undefined, this);
2696
+ }
2697
+ return /* @__PURE__ */ jsxDEV3("div", {
2698
+ className: "space-y-6",
2699
+ children: [
2700
+ stats && /* @__PURE__ */ jsxDEV3(StatCardGroup, {
2701
+ children: [
2702
+ /* @__PURE__ */ jsxDEV3(StatCard, {
2703
+ label: "Total Projects",
2704
+ value: stats.total.toString()
2705
+ }, undefined, false, undefined, this),
2706
+ /* @__PURE__ */ jsxDEV3(StatCard, {
2707
+ label: "Active",
2708
+ value: stats.activeCount.toString()
2709
+ }, undefined, false, undefined, this),
2710
+ /* @__PURE__ */ jsxDEV3(StatCard, {
2711
+ label: "Draft",
2712
+ value: stats.draftCount.toString()
2713
+ }, undefined, false, undefined, this)
2714
+ ]
2715
+ }, undefined, true, undefined, this),
2716
+ /* @__PURE__ */ jsxDEV3("div", {
2717
+ className: "grid gap-4 md:grid-cols-2 lg:grid-cols-3",
2718
+ children: data.items.map((project) => /* @__PURE__ */ jsxDEV3(EntityCard, {
2719
+ cardTitle: project.name,
2720
+ cardSubtitle: project.tier,
2721
+ meta: /* @__PURE__ */ jsxDEV3("p", {
2722
+ className: "text-muted-foreground text-sm",
2723
+ children: project.description
2724
+ }, undefined, false, undefined, this),
2725
+ chips: /* @__PURE__ */ jsxDEV3(StatusChip, {
2726
+ tone: getStatusTone(project.status),
2727
+ label: project.status
2728
+ }, undefined, false, undefined, this),
2729
+ footer: /* @__PURE__ */ jsxDEV3("span", {
2730
+ className: "text-muted-foreground text-xs",
2731
+ children: project.updatedAt.toLocaleDateString()
2732
+ }, undefined, false, undefined, this),
2733
+ onClick: onProjectClick ? () => onProjectClick(project.id) : undefined
2734
+ }, project.id, false, undefined, this))
2735
+ }, undefined, false, undefined, this)
2736
+ ]
2737
+ }, undefined, true, undefined, this);
2738
+ }
2739
+
2740
+ // src/ui/renderers/project-list.renderer.tsx
2741
+ import { jsxDEV as jsxDEV4 } from "react/jsx-dev-runtime";
2742
+ var projectListReactRenderer = {
2743
+ target: "react",
2744
+ render: async (desc, _ctx) => {
2745
+ if (desc.source.type !== "component") {
2746
+ throw new Error("Invalid source type");
2747
+ }
2748
+ if (desc.source.componentKey !== "SaasProjectListView") {
2749
+ throw new Error(`Unknown component: ${desc.source.componentKey}`);
2750
+ }
2751
+ return /* @__PURE__ */ jsxDEV4(SaasProjectList, {}, undefined, false, undefined, this);
2752
+ }
2753
+ };
2754
+ // src/ui/SaasDashboard.tsx
2755
+ import {
2756
+ Button as Button4,
2757
+ EmptyState as EmptyState2,
2758
+ EntityCard as EntityCard2,
2759
+ ErrorState as ErrorState2,
2760
+ LoaderBlock as LoaderBlock2,
2761
+ StatCard as StatCard2,
2762
+ StatCardGroup as StatCardGroup2,
2763
+ StatusChip as StatusChip2
2764
+ } from "@contractspec/lib.design-system";
2765
+ import { useCallback as useCallback3, useState as useState5 } from "react";
2766
+ import { jsxDEV as jsxDEV5 } from "react/jsx-dev-runtime";
2767
+ "use client";
2768
+ function getStatusTone2(status) {
2769
+ switch (status) {
2770
+ case "ACTIVE":
2771
+ return "success";
2772
+ case "DRAFT":
2773
+ return "neutral";
2774
+ case "ARCHIVED":
2775
+ return "warning";
2776
+ default:
2777
+ return "neutral";
2778
+ }
2779
+ }
2780
+ function SaasDashboard() {
2781
+ const [activeTab, setActiveTab] = useState5("projects");
2782
+ const [isCreateModalOpen, setIsCreateModalOpen] = useState5(false);
2783
+ const [selectedProject, setSelectedProject] = useState5(null);
2784
+ const [isProjectActionsOpen, setIsProjectActionsOpen] = useState5(false);
2785
+ const { data, subscription, loading, error, stats, refetch } = useProjectList();
2786
+ const mutations = useProjectMutations({
2787
+ onSuccess: () => {
2788
+ refetch();
2789
+ }
2790
+ });
2791
+ const handleProjectClick = useCallback3((project) => {
2792
+ setSelectedProject(project);
2793
+ setIsProjectActionsOpen(true);
2794
+ }, []);
2795
+ const tabs = [
2796
+ { id: "projects", label: "Projects", icon: "\uD83D\uDCC1" },
2797
+ { id: "billing", label: "Billing", icon: "\uD83D\uDCB3" },
2798
+ { id: "settings", label: "Settings", icon: "\u2699\uFE0F" }
2799
+ ];
2800
+ if (loading && !data) {
2801
+ return /* @__PURE__ */ jsxDEV5(LoaderBlock2, {
2802
+ label: "Loading dashboard..."
2803
+ }, undefined, false, undefined, this);
2804
+ }
2805
+ if (error) {
2806
+ return /* @__PURE__ */ jsxDEV5(ErrorState2, {
2807
+ title: "Failed to load dashboard",
2808
+ description: error.message,
2809
+ onRetry: refetch,
2810
+ retryLabel: "Retry"
2811
+ }, undefined, false, undefined, this);
2812
+ }
2813
+ return /* @__PURE__ */ jsxDEV5("div", {
2814
+ className: "space-y-6",
2815
+ children: [
2816
+ /* @__PURE__ */ jsxDEV5("div", {
2817
+ className: "flex items-center justify-between",
2818
+ children: [
2819
+ /* @__PURE__ */ jsxDEV5("h2", {
2820
+ className: "font-bold text-2xl",
2821
+ children: "SaaS Dashboard"
2822
+ }, undefined, false, undefined, this),
2823
+ activeTab === "projects" && /* @__PURE__ */ jsxDEV5(Button4, {
2824
+ onPress: () => setIsCreateModalOpen(true),
2825
+ children: [
2826
+ /* @__PURE__ */ jsxDEV5("span", {
2827
+ className: "mr-2",
2828
+ children: "+"
2829
+ }, undefined, false, undefined, this),
2830
+ " New Project"
2831
+ ]
2832
+ }, undefined, true, undefined, this)
2833
+ ]
2834
+ }, undefined, true, undefined, this),
2835
+ stats && subscription && /* @__PURE__ */ jsxDEV5(StatCardGroup2, {
2836
+ children: [
2837
+ /* @__PURE__ */ jsxDEV5(StatCard2, {
2838
+ label: "Projects",
2839
+ value: stats.total.toString()
2840
+ }, undefined, false, undefined, this),
2841
+ /* @__PURE__ */ jsxDEV5(StatCard2, {
2842
+ label: "Active",
2843
+ value: stats.activeCount.toString()
2844
+ }, undefined, false, undefined, this),
2845
+ /* @__PURE__ */ jsxDEV5(StatCard2, {
2846
+ label: "Draft",
2847
+ value: stats.draftCount.toString()
2848
+ }, undefined, false, undefined, this),
2849
+ /* @__PURE__ */ jsxDEV5(StatCard2, {
2850
+ label: "Plan",
2851
+ value: subscription.plan,
2852
+ hint: subscription.status
2853
+ }, undefined, false, undefined, this)
2854
+ ]
2855
+ }, undefined, true, undefined, this),
2856
+ /* @__PURE__ */ jsxDEV5("nav", {
2857
+ className: "flex gap-1 rounded-lg bg-muted p-1",
2858
+ role: "tablist",
2859
+ children: tabs.map((tab) => /* @__PURE__ */ jsxDEV5("button", {
2860
+ type: "button",
2861
+ role: "tab",
2862
+ "aria-selected": activeTab === tab.id,
2863
+ onClick: () => setActiveTab(tab.id),
2864
+ className: `flex flex-1 items-center justify-center gap-2 rounded-md px-4 py-2 font-medium text-sm transition-colors ${activeTab === tab.id ? "bg-background text-foreground shadow-sm" : "text-muted-foreground hover:text-foreground"}`,
2865
+ children: [
2866
+ /* @__PURE__ */ jsxDEV5("span", {
2867
+ children: tab.icon
2551
2868
  }, undefined, false, undefined, this),
2552
2869
  tab.label
2553
2870
  ]
2554
2871
  }, tab.id, true, undefined, this))
2555
2872
  }, undefined, false, undefined, this),
2556
- /* @__PURE__ */ jsxDEV3("div", {
2873
+ /* @__PURE__ */ jsxDEV5("div", {
2557
2874
  className: "min-h-[400px]",
2558
2875
  role: "tabpanel",
2559
2876
  children: [
2560
- activeTab === "projects" && /* @__PURE__ */ jsxDEV3(ProjectsTab, {
2877
+ activeTab === "projects" && /* @__PURE__ */ jsxDEV5(ProjectsTab, {
2561
2878
  data,
2562
2879
  onProjectClick: handleProjectClick
2563
2880
  }, undefined, false, undefined, this),
2564
- activeTab === "billing" && /* @__PURE__ */ jsxDEV3(BillingTab, {
2881
+ activeTab === "billing" && /* @__PURE__ */ jsxDEV5(BillingTab, {
2565
2882
  subscription
2566
2883
  }, undefined, false, undefined, this),
2567
- activeTab === "settings" && /* @__PURE__ */ jsxDEV3(SettingsTab, {}, undefined, false, undefined, this)
2884
+ activeTab === "settings" && /* @__PURE__ */ jsxDEV5(SettingsTab, {}, undefined, false, undefined, this)
2568
2885
  ]
2569
2886
  }, undefined, true, undefined, this),
2570
- /* @__PURE__ */ jsxDEV3(CreateProjectModal, {
2887
+ /* @__PURE__ */ jsxDEV5(CreateProjectModal, {
2571
2888
  isOpen: isCreateModalOpen,
2572
2889
  onClose: () => setIsCreateModalOpen(false),
2573
2890
  onSubmit: async (input) => {
@@ -2575,7 +2892,7 @@ function SaasDashboard() {
2575
2892
  },
2576
2893
  isLoading: mutations.createState.loading
2577
2894
  }, undefined, false, undefined, this),
2578
- /* @__PURE__ */ jsxDEV3(ProjectActionsModal, {
2895
+ /* @__PURE__ */ jsxDEV5(ProjectActionsModal, {
2579
2896
  isOpen: isProjectActionsOpen,
2580
2897
  project: selectedProject,
2581
2898
  onClose: () => {
@@ -2601,34 +2918,34 @@ function SaasDashboard() {
2601
2918
  }
2602
2919
  function ProjectsTab({ data, onProjectClick }) {
2603
2920
  if (!data?.items.length) {
2604
- return /* @__PURE__ */ jsxDEV3(EmptyState, {
2921
+ return /* @__PURE__ */ jsxDEV5(EmptyState2, {
2605
2922
  title: "No projects yet",
2606
2923
  description: "Create your first project to get started."
2607
2924
  }, undefined, false, undefined, this);
2608
2925
  }
2609
- return /* @__PURE__ */ jsxDEV3("div", {
2926
+ return /* @__PURE__ */ jsxDEV5("div", {
2610
2927
  className: "space-y-4",
2611
- children: /* @__PURE__ */ jsxDEV3("div", {
2928
+ children: /* @__PURE__ */ jsxDEV5("div", {
2612
2929
  className: "grid gap-4 md:grid-cols-2 lg:grid-cols-3",
2613
- children: data.items.map((project) => /* @__PURE__ */ jsxDEV3(EntityCard, {
2930
+ children: data.items.map((project) => /* @__PURE__ */ jsxDEV5(EntityCard2, {
2614
2931
  cardTitle: project.name,
2615
2932
  cardSubtitle: project.tier,
2616
- meta: /* @__PURE__ */ jsxDEV3("p", {
2933
+ meta: /* @__PURE__ */ jsxDEV5("p", {
2617
2934
  className: "text-muted-foreground text-sm",
2618
2935
  children: project.description
2619
2936
  }, undefined, false, undefined, this),
2620
- chips: /* @__PURE__ */ jsxDEV3(StatusChip, {
2621
- tone: getStatusTone(project.status),
2937
+ chips: /* @__PURE__ */ jsxDEV5(StatusChip2, {
2938
+ tone: getStatusTone2(project.status),
2622
2939
  label: project.status
2623
2940
  }, undefined, false, undefined, this),
2624
- footer: /* @__PURE__ */ jsxDEV3("div", {
2941
+ footer: /* @__PURE__ */ jsxDEV5("div", {
2625
2942
  className: "flex w-full items-center justify-between",
2626
2943
  children: [
2627
- /* @__PURE__ */ jsxDEV3("span", {
2944
+ /* @__PURE__ */ jsxDEV5("span", {
2628
2945
  className: "text-muted-foreground text-xs",
2629
2946
  children: project.updatedAt.toLocaleDateString()
2630
2947
  }, undefined, false, undefined, this),
2631
- /* @__PURE__ */ jsxDEV3(Button3, {
2948
+ /* @__PURE__ */ jsxDEV5(Button4, {
2632
2949
  variant: "ghost",
2633
2950
  size: "sm",
2634
2951
  onPress: () => onProjectClick?.(project),
@@ -2643,25 +2960,25 @@ function ProjectsTab({ data, onProjectClick }) {
2643
2960
  function BillingTab({ subscription }) {
2644
2961
  if (!subscription)
2645
2962
  return null;
2646
- return /* @__PURE__ */ jsxDEV3("div", {
2963
+ return /* @__PURE__ */ jsxDEV5("div", {
2647
2964
  className: "space-y-6",
2648
2965
  children: [
2649
- /* @__PURE__ */ jsxDEV3("div", {
2650
- className: "border-border bg-card rounded-xl border p-6",
2966
+ /* @__PURE__ */ jsxDEV5("div", {
2967
+ className: "rounded-xl border border-border bg-card p-6",
2651
2968
  children: [
2652
- /* @__PURE__ */ jsxDEV3("div", {
2969
+ /* @__PURE__ */ jsxDEV5("div", {
2653
2970
  className: "flex items-start justify-between",
2654
2971
  children: [
2655
- /* @__PURE__ */ jsxDEV3("div", {
2972
+ /* @__PURE__ */ jsxDEV5("div", {
2656
2973
  children: [
2657
- /* @__PURE__ */ jsxDEV3("h3", {
2658
- className: "text-lg font-semibold",
2974
+ /* @__PURE__ */ jsxDEV5("h3", {
2975
+ className: "font-semibold text-lg",
2659
2976
  children: [
2660
2977
  subscription.plan,
2661
2978
  " Plan"
2662
2979
  ]
2663
2980
  }, undefined, true, undefined, this),
2664
- /* @__PURE__ */ jsxDEV3("p", {
2981
+ /* @__PURE__ */ jsxDEV5("p", {
2665
2982
  className: "text-muted-foreground text-sm",
2666
2983
  children: [
2667
2984
  "Current period:",
@@ -2672,7 +2989,7 @@ function BillingTab({ subscription }) {
2672
2989
  subscription.currentPeriodEnd.toLocaleDateString()
2673
2990
  ]
2674
2991
  }, undefined, true, undefined, this),
2675
- /* @__PURE__ */ jsxDEV3("p", {
2992
+ /* @__PURE__ */ jsxDEV5("p", {
2676
2993
  className: "text-muted-foreground text-sm",
2677
2994
  children: [
2678
2995
  "Billing cycle: ",
@@ -2681,21 +2998,21 @@ function BillingTab({ subscription }) {
2681
2998
  }, undefined, true, undefined, this)
2682
2999
  ]
2683
3000
  }, undefined, true, undefined, this),
2684
- /* @__PURE__ */ jsxDEV3(StatusChip, {
3001
+ /* @__PURE__ */ jsxDEV5(StatusChip2, {
2685
3002
  tone: "success",
2686
3003
  label: subscription.status
2687
3004
  }, undefined, false, undefined, this)
2688
3005
  ]
2689
3006
  }, undefined, true, undefined, this),
2690
- /* @__PURE__ */ jsxDEV3("div", {
3007
+ /* @__PURE__ */ jsxDEV5("div", {
2691
3008
  className: "mt-4 flex gap-3",
2692
3009
  children: [
2693
- /* @__PURE__ */ jsxDEV3(Button3, {
3010
+ /* @__PURE__ */ jsxDEV5(Button4, {
2694
3011
  variant: "outline",
2695
3012
  onPress: () => alert("Upgrade clicked!"),
2696
3013
  children: "Upgrade Plan"
2697
3014
  }, undefined, false, undefined, this),
2698
- /* @__PURE__ */ jsxDEV3(Button3, {
3015
+ /* @__PURE__ */ jsxDEV5(Button4, {
2699
3016
  variant: "ghost",
2700
3017
  onPress: () => alert("Manage Billing clicked!"),
2701
3018
  children: "Manage Billing"
@@ -2704,10 +3021,10 @@ function BillingTab({ subscription }) {
2704
3021
  }, undefined, true, undefined, this)
2705
3022
  ]
2706
3023
  }, undefined, true, undefined, this),
2707
- subscription.cancelAtPeriodEnd && /* @__PURE__ */ jsxDEV3("div", {
2708
- className: "border-border bg-destructive/10 text-destructive rounded-xl border p-4",
2709
- children: /* @__PURE__ */ jsxDEV3("p", {
2710
- className: "text-sm font-medium",
3024
+ subscription.cancelAtPeriodEnd && /* @__PURE__ */ jsxDEV5("div", {
3025
+ className: "rounded-xl border border-border bg-destructive/10 p-4 text-destructive",
3026
+ children: /* @__PURE__ */ jsxDEV5("p", {
3027
+ className: "font-medium text-sm",
2711
3028
  children: "\u26A0\uFE0F Your subscription will be cancelled at the end of the current period."
2712
3029
  }, undefined, false, undefined, this)
2713
3030
  }, undefined, false, undefined, this)
@@ -2715,233 +3032,137 @@ function BillingTab({ subscription }) {
2715
3032
  }, undefined, true, undefined, this);
2716
3033
  }
2717
3034
  function SettingsTab() {
2718
- return /* @__PURE__ */ jsxDEV3("div", {
3035
+ return /* @__PURE__ */ jsxDEV5("div", {
2719
3036
  className: "space-y-6",
2720
- children: /* @__PURE__ */ jsxDEV3("div", {
2721
- className: "border-border bg-card rounded-xl border p-6",
3037
+ children: /* @__PURE__ */ jsxDEV5("div", {
3038
+ className: "rounded-xl border border-border bg-card p-6",
2722
3039
  children: [
2723
- /* @__PURE__ */ jsxDEV3("h3", {
2724
- className: "mb-4 text-lg font-semibold",
3040
+ /* @__PURE__ */ jsxDEV5("h3", {
3041
+ className: "mb-4 font-semibold text-lg",
2725
3042
  children: "Organization Settings"
2726
3043
  }, undefined, false, undefined, this),
2727
- /* @__PURE__ */ jsxDEV3("div", {
3044
+ /* @__PURE__ */ jsxDEV5("div", {
2728
3045
  className: "space-y-4",
2729
3046
  children: [
2730
- /* @__PURE__ */ jsxDEV3("div", {
3047
+ /* @__PURE__ */ jsxDEV5("div", {
2731
3048
  children: [
2732
- /* @__PURE__ */ jsxDEV3("label", {
3049
+ /* @__PURE__ */ jsxDEV5("label", {
2733
3050
  htmlFor: "org-name",
2734
- className: "text-sm font-medium",
3051
+ className: "font-medium text-sm",
2735
3052
  children: "Organization Name"
2736
3053
  }, undefined, false, undefined, this),
2737
- /* @__PURE__ */ jsxDEV3("input", {
3054
+ /* @__PURE__ */ jsxDEV5("input", {
2738
3055
  id: "org-name",
2739
3056
  type: "text",
2740
3057
  defaultValue: "Demo Organization",
2741
- className: "border-input bg-background mt-1 block w-full rounded-md border px-3 py-2"
3058
+ className: "mt-1 block w-full rounded-md border border-input bg-background px-3 py-2"
2742
3059
  }, undefined, false, undefined, this)
2743
3060
  ]
2744
3061
  }, undefined, true, undefined, this),
2745
- /* @__PURE__ */ jsxDEV3("div", {
3062
+ /* @__PURE__ */ jsxDEV5("div", {
2746
3063
  children: [
2747
- /* @__PURE__ */ jsxDEV3("label", {
3064
+ /* @__PURE__ */ jsxDEV5("label", {
2748
3065
  htmlFor: "timezone",
2749
- className: "text-sm font-medium",
3066
+ className: "font-medium text-sm",
2750
3067
  children: "Default Timezone"
2751
3068
  }, undefined, false, undefined, this),
2752
- /* @__PURE__ */ jsxDEV3("select", {
3069
+ /* @__PURE__ */ jsxDEV5("select", {
2753
3070
  id: "timezone",
2754
- className: "border-input bg-background mt-1 block w-full rounded-md border px-3 py-2",
3071
+ className: "mt-1 block w-full rounded-md border border-input bg-background px-3 py-2",
2755
3072
  children: [
2756
- /* @__PURE__ */ jsxDEV3("option", {
3073
+ /* @__PURE__ */ jsxDEV5("option", {
2757
3074
  children: "UTC"
2758
3075
  }, undefined, false, undefined, this),
2759
- /* @__PURE__ */ jsxDEV3("option", {
2760
- children: "America/New_York"
2761
- }, undefined, false, undefined, this),
2762
- /* @__PURE__ */ jsxDEV3("option", {
2763
- children: "Europe/London"
2764
- }, undefined, false, undefined, this),
2765
- /* @__PURE__ */ jsxDEV3("option", {
2766
- children: "Asia/Tokyo"
2767
- }, undefined, false, undefined, this)
2768
- ]
2769
- }, undefined, true, undefined, this)
2770
- ]
2771
- }, undefined, true, undefined, this),
2772
- /* @__PURE__ */ jsxDEV3("div", {
2773
- className: "pt-2",
2774
- children: /* @__PURE__ */ jsxDEV3(Button3, {
2775
- onPress: () => alert("Settings saved!"),
2776
- children: "Save Settings"
2777
- }, undefined, false, undefined, this)
2778
- }, undefined, false, undefined, this)
2779
- ]
2780
- }, undefined, true, undefined, this)
2781
- ]
2782
- }, undefined, true, undefined, this)
2783
- }, undefined, false, undefined, this);
2784
- }
2785
-
2786
- // src/ui/SaasProjectList.tsx
2787
- import {
2788
- StatCard as StatCard2,
2789
- StatCardGroup as StatCardGroup2,
2790
- StatusChip as StatusChip2,
2791
- EntityCard as EntityCard2,
2792
- EmptyState as EmptyState2,
2793
- LoaderBlock as LoaderBlock2,
2794
- ErrorState as ErrorState2,
2795
- Button as Button4
2796
- } from "@contractspec/lib.design-system";
2797
- import { jsxDEV as jsxDEV4 } from "react/jsx-dev-runtime";
2798
- "use client";
2799
- function getStatusTone2(status) {
2800
- switch (status) {
2801
- case "ACTIVE":
2802
- return "success";
2803
- case "DRAFT":
2804
- return "neutral";
2805
- case "ARCHIVED":
2806
- return "danger";
2807
- default:
2808
- return "neutral";
2809
- }
2810
- }
2811
- function SaasProjectList({
2812
- onProjectClick,
2813
- onCreateProject
2814
- }) {
2815
- const { data, loading, error, stats, refetch } = useProjectList();
2816
- if (loading && !data) {
2817
- return /* @__PURE__ */ jsxDEV4(LoaderBlock2, {
2818
- label: "Loading projects..."
2819
- }, undefined, false, undefined, this);
2820
- }
2821
- if (error) {
2822
- return /* @__PURE__ */ jsxDEV4(ErrorState2, {
2823
- title: "Failed to load projects",
2824
- description: error.message,
2825
- onRetry: refetch,
2826
- retryLabel: "Retry"
2827
- }, undefined, false, undefined, this);
2828
- }
2829
- if (!data?.items.length) {
2830
- return /* @__PURE__ */ jsxDEV4(EmptyState2, {
2831
- title: "No projects found",
2832
- description: "Create your first project to get started.",
2833
- primaryAction: onCreateProject ? /* @__PURE__ */ jsxDEV4(Button4, {
2834
- onPress: onCreateProject,
2835
- children: "Create Project"
2836
- }, undefined, false, undefined, this) : undefined
2837
- }, undefined, false, undefined, this);
2838
- }
2839
- return /* @__PURE__ */ jsxDEV4("div", {
2840
- className: "space-y-6",
2841
- children: [
2842
- stats && /* @__PURE__ */ jsxDEV4(StatCardGroup2, {
2843
- children: [
2844
- /* @__PURE__ */ jsxDEV4(StatCard2, {
2845
- label: "Total Projects",
2846
- value: stats.total.toString()
2847
- }, undefined, false, undefined, this),
2848
- /* @__PURE__ */ jsxDEV4(StatCard2, {
2849
- label: "Active",
2850
- value: stats.activeCount.toString()
2851
- }, undefined, false, undefined, this),
2852
- /* @__PURE__ */ jsxDEV4(StatCard2, {
2853
- label: "Draft",
2854
- value: stats.draftCount.toString()
2855
- }, undefined, false, undefined, this)
2856
- ]
2857
- }, undefined, true, undefined, this),
2858
- /* @__PURE__ */ jsxDEV4("div", {
2859
- className: "grid gap-4 md:grid-cols-2 lg:grid-cols-3",
2860
- children: data.items.map((project) => /* @__PURE__ */ jsxDEV4(EntityCard2, {
2861
- cardTitle: project.name,
2862
- cardSubtitle: project.tier,
2863
- meta: /* @__PURE__ */ jsxDEV4("p", {
2864
- className: "text-muted-foreground text-sm",
2865
- children: project.description
2866
- }, undefined, false, undefined, this),
2867
- chips: /* @__PURE__ */ jsxDEV4(StatusChip2, {
2868
- tone: getStatusTone2(project.status),
2869
- label: project.status
2870
- }, undefined, false, undefined, this),
2871
- footer: /* @__PURE__ */ jsxDEV4("span", {
2872
- className: "text-muted-foreground text-xs",
2873
- children: project.updatedAt.toLocaleDateString()
2874
- }, undefined, false, undefined, this),
2875
- onClick: onProjectClick ? () => onProjectClick(project.id) : undefined
2876
- }, project.id, false, undefined, this))
2877
- }, undefined, false, undefined, this)
2878
- ]
2879
- }, undefined, true, undefined, this);
3076
+ /* @__PURE__ */ jsxDEV5("option", {
3077
+ children: "America/New_York"
3078
+ }, undefined, false, undefined, this),
3079
+ /* @__PURE__ */ jsxDEV5("option", {
3080
+ children: "Europe/London"
3081
+ }, undefined, false, undefined, this),
3082
+ /* @__PURE__ */ jsxDEV5("option", {
3083
+ children: "Asia/Tokyo"
3084
+ }, undefined, false, undefined, this)
3085
+ ]
3086
+ }, undefined, true, undefined, this)
3087
+ ]
3088
+ }, undefined, true, undefined, this),
3089
+ /* @__PURE__ */ jsxDEV5("div", {
3090
+ className: "pt-2",
3091
+ children: /* @__PURE__ */ jsxDEV5(Button4, {
3092
+ onPress: () => alert("Settings saved!"),
3093
+ children: "Save Settings"
3094
+ }, undefined, false, undefined, this)
3095
+ }, undefined, false, undefined, this)
3096
+ ]
3097
+ }, undefined, true, undefined, this)
3098
+ ]
3099
+ }, undefined, true, undefined, this)
3100
+ }, undefined, false, undefined, this);
2880
3101
  }
2881
3102
 
2882
3103
  // src/ui/SaasSettingsPanel.tsx
2883
- import { useState as useState6 } from "react";
2884
3104
  import { Button as Button5 } from "@contractspec/lib.design-system";
2885
- import { jsxDEV as jsxDEV5 } from "react/jsx-dev-runtime";
3105
+ import { useState as useState6 } from "react";
3106
+ import { jsxDEV as jsxDEV6 } from "react/jsx-dev-runtime";
2886
3107
  "use client";
2887
3108
  function SaasSettingsPanel() {
2888
3109
  const [orgName, setOrgName] = useState6("Demo Organization");
2889
3110
  const [timezone, setTimezone] = useState6("UTC");
2890
- return /* @__PURE__ */ jsxDEV5("div", {
3111
+ return /* @__PURE__ */ jsxDEV6("div", {
2891
3112
  className: "space-y-6",
2892
3113
  children: [
2893
- /* @__PURE__ */ jsxDEV5("div", {
2894
- className: "border-border bg-card rounded-xl border p-6",
3114
+ /* @__PURE__ */ jsxDEV6("div", {
3115
+ className: "rounded-xl border border-border bg-card p-6",
2895
3116
  children: [
2896
- /* @__PURE__ */ jsxDEV5("h3", {
2897
- className: "mb-4 text-lg font-semibold",
3117
+ /* @__PURE__ */ jsxDEV6("h3", {
3118
+ className: "mb-4 font-semibold text-lg",
2898
3119
  children: "Organization Settings"
2899
3120
  }, undefined, false, undefined, this),
2900
- /* @__PURE__ */ jsxDEV5("div", {
3121
+ /* @__PURE__ */ jsxDEV6("div", {
2901
3122
  className: "space-y-4",
2902
3123
  children: [
2903
- /* @__PURE__ */ jsxDEV5("div", {
3124
+ /* @__PURE__ */ jsxDEV6("div", {
2904
3125
  children: [
2905
- /* @__PURE__ */ jsxDEV5("label", {
3126
+ /* @__PURE__ */ jsxDEV6("label", {
2906
3127
  htmlFor: "setting-org-name",
2907
- className: "block text-sm font-medium",
3128
+ className: "block font-medium text-sm",
2908
3129
  children: "Organization Name"
2909
3130
  }, undefined, false, undefined, this),
2910
- /* @__PURE__ */ jsxDEV5("input", {
3131
+ /* @__PURE__ */ jsxDEV6("input", {
2911
3132
  id: "setting-org-name",
2912
3133
  type: "text",
2913
3134
  value: orgName,
2914
3135
  onChange: (e) => setOrgName(e.target.value),
2915
- className: "border-input bg-background mt-1 block w-full rounded-md border px-3 py-2"
3136
+ className: "mt-1 block w-full rounded-md border border-input bg-background px-3 py-2"
2916
3137
  }, undefined, false, undefined, this)
2917
3138
  ]
2918
3139
  }, undefined, true, undefined, this),
2919
- /* @__PURE__ */ jsxDEV5("div", {
3140
+ /* @__PURE__ */ jsxDEV6("div", {
2920
3141
  children: [
2921
- /* @__PURE__ */ jsxDEV5("label", {
3142
+ /* @__PURE__ */ jsxDEV6("label", {
2922
3143
  htmlFor: "setting-timezone",
2923
- className: "block text-sm font-medium",
3144
+ className: "block font-medium text-sm",
2924
3145
  children: "Default Timezone"
2925
3146
  }, undefined, false, undefined, this),
2926
- /* @__PURE__ */ jsxDEV5("select", {
3147
+ /* @__PURE__ */ jsxDEV6("select", {
2927
3148
  id: "setting-timezone",
2928
3149
  value: timezone,
2929
3150
  onChange: (e) => setTimezone(e.target.value),
2930
- className: "border-input bg-background mt-1 block w-full rounded-md border px-3 py-2",
3151
+ className: "mt-1 block w-full rounded-md border border-input bg-background px-3 py-2",
2931
3152
  children: [
2932
- /* @__PURE__ */ jsxDEV5("option", {
3153
+ /* @__PURE__ */ jsxDEV6("option", {
2933
3154
  value: "UTC",
2934
3155
  children: "UTC"
2935
3156
  }, undefined, false, undefined, this),
2936
- /* @__PURE__ */ jsxDEV5("option", {
3157
+ /* @__PURE__ */ jsxDEV6("option", {
2937
3158
  value: "America/New_York",
2938
3159
  children: "America/New_York"
2939
3160
  }, undefined, false, undefined, this),
2940
- /* @__PURE__ */ jsxDEV5("option", {
3161
+ /* @__PURE__ */ jsxDEV6("option", {
2941
3162
  value: "Europe/London",
2942
3163
  children: "Europe/London"
2943
3164
  }, undefined, false, undefined, this),
2944
- /* @__PURE__ */ jsxDEV5("option", {
3165
+ /* @__PURE__ */ jsxDEV6("option", {
2945
3166
  value: "Asia/Tokyo",
2946
3167
  children: "Asia/Tokyo"
2947
3168
  }, undefined, false, undefined, this)
@@ -2951,37 +3172,37 @@ function SaasSettingsPanel() {
2951
3172
  }, undefined, true, undefined, this)
2952
3173
  ]
2953
3174
  }, undefined, true, undefined, this),
2954
- /* @__PURE__ */ jsxDEV5("div", {
3175
+ /* @__PURE__ */ jsxDEV6("div", {
2955
3176
  className: "mt-6",
2956
- children: /* @__PURE__ */ jsxDEV5(Button5, {
3177
+ children: /* @__PURE__ */ jsxDEV6(Button5, {
2957
3178
  variant: "default",
2958
3179
  children: "Save Changes"
2959
3180
  }, undefined, false, undefined, this)
2960
3181
  }, undefined, false, undefined, this)
2961
3182
  ]
2962
3183
  }, undefined, true, undefined, this),
2963
- /* @__PURE__ */ jsxDEV5("div", {
2964
- className: "border-border bg-card rounded-xl border p-6",
3184
+ /* @__PURE__ */ jsxDEV6("div", {
3185
+ className: "rounded-xl border border-border bg-card p-6",
2965
3186
  children: [
2966
- /* @__PURE__ */ jsxDEV5("h3", {
2967
- className: "mb-4 text-lg font-semibold",
3187
+ /* @__PURE__ */ jsxDEV6("h3", {
3188
+ className: "mb-4 font-semibold text-lg",
2968
3189
  children: "Notifications"
2969
3190
  }, undefined, false, undefined, this),
2970
- /* @__PURE__ */ jsxDEV5("div", {
3191
+ /* @__PURE__ */ jsxDEV6("div", {
2971
3192
  className: "space-y-3",
2972
3193
  children: [
2973
3194
  { label: "Email notifications", defaultChecked: true },
2974
3195
  { label: "Usage alerts", defaultChecked: true },
2975
3196
  { label: "Weekly digest", defaultChecked: false }
2976
- ].map((item) => /* @__PURE__ */ jsxDEV5("label", {
3197
+ ].map((item) => /* @__PURE__ */ jsxDEV6("label", {
2977
3198
  className: "flex items-center gap-3",
2978
3199
  children: [
2979
- /* @__PURE__ */ jsxDEV5("input", {
3200
+ /* @__PURE__ */ jsxDEV6("input", {
2980
3201
  type: "checkbox",
2981
3202
  defaultChecked: item.defaultChecked,
2982
- className: "border-input h-4 w-4 rounded"
3203
+ className: "h-4 w-4 rounded border-input"
2983
3204
  }, undefined, false, undefined, this),
2984
- /* @__PURE__ */ jsxDEV5("span", {
3205
+ /* @__PURE__ */ jsxDEV6("span", {
2985
3206
  className: "text-sm",
2986
3207
  children: item.label
2987
3208
  }, undefined, false, undefined, this)
@@ -2990,26 +3211,26 @@ function SaasSettingsPanel() {
2990
3211
  }, undefined, false, undefined, this)
2991
3212
  ]
2992
3213
  }, undefined, true, undefined, this),
2993
- /* @__PURE__ */ jsxDEV5("div", {
3214
+ /* @__PURE__ */ jsxDEV6("div", {
2994
3215
  className: "rounded-xl border border-red-200 bg-red-50 p-6 dark:border-red-900 dark:bg-red-950/20",
2995
3216
  children: [
2996
- /* @__PURE__ */ jsxDEV5("h3", {
2997
- className: "mb-2 text-lg font-semibold text-red-700 dark:text-red-400",
3217
+ /* @__PURE__ */ jsxDEV6("h3", {
3218
+ className: "mb-2 font-semibold text-lg text-red-700 dark:text-red-400",
2998
3219
  children: "Danger Zone"
2999
3220
  }, undefined, false, undefined, this),
3000
- /* @__PURE__ */ jsxDEV5("p", {
3001
- className: "mb-4 text-sm text-red-600 dark:text-red-300",
3221
+ /* @__PURE__ */ jsxDEV6("p", {
3222
+ className: "mb-4 text-red-600 text-sm dark:text-red-300",
3002
3223
  children: "These actions are irreversible. Please proceed with caution."
3003
3224
  }, undefined, false, undefined, this),
3004
- /* @__PURE__ */ jsxDEV5("div", {
3225
+ /* @__PURE__ */ jsxDEV6("div", {
3005
3226
  className: "flex gap-3",
3006
3227
  children: [
3007
- /* @__PURE__ */ jsxDEV5(Button5, {
3228
+ /* @__PURE__ */ jsxDEV6(Button5, {
3008
3229
  variant: "secondary",
3009
3230
  size: "sm",
3010
3231
  children: "Export Data"
3011
3232
  }, undefined, false, undefined, this),
3012
- /* @__PURE__ */ jsxDEV5(Button5, {
3233
+ /* @__PURE__ */ jsxDEV6(Button5, {
3013
3234
  variant: "secondary",
3014
3235
  size: "sm",
3015
3236
  children: "Delete Organization"
@@ -3021,228 +3242,6 @@ function SaasSettingsPanel() {
3021
3242
  ]
3022
3243
  }, undefined, true, undefined, this);
3023
3244
  }
3024
- // src/ui/hooks/index.ts
3025
- "use client";
3026
-
3027
- // src/ui/renderers/project-list.renderer.tsx
3028
- import { jsxDEV as jsxDEV6 } from "react/jsx-dev-runtime";
3029
- var projectListReactRenderer = {
3030
- target: "react",
3031
- render: async (desc, _ctx) => {
3032
- if (desc.source.type !== "component") {
3033
- throw new Error("Invalid source type");
3034
- }
3035
- if (desc.source.componentKey !== "SaasProjectListView") {
3036
- throw new Error(`Unknown component: ${desc.source.componentKey}`);
3037
- }
3038
- return /* @__PURE__ */ jsxDEV6(SaasProjectList, {}, undefined, false, undefined, this);
3039
- }
3040
- };
3041
-
3042
- // src/ui/renderers/project-list.markdown.ts
3043
- var projectListMarkdownRenderer = {
3044
- target: "markdown",
3045
- render: async (desc, _ctx) => {
3046
- if (desc.source.type !== "component" || desc.source.componentKey !== "ProjectListView") {
3047
- throw new Error("projectListMarkdownRenderer: not ProjectListView");
3048
- }
3049
- const data = await mockListProjectsHandler({
3050
- limit: 20,
3051
- offset: 0
3052
- });
3053
- const items = data.projects ?? data.items ?? [];
3054
- const lines = [
3055
- "# Projects",
3056
- "",
3057
- `**Total**: ${data.total} projects`,
3058
- ""
3059
- ];
3060
- if (items.length === 0) {
3061
- lines.push("_No projects found._");
3062
- } else {
3063
- lines.push("| Status | Project | Description |");
3064
- lines.push("|--------|---------|-------------|");
3065
- for (const project of items) {
3066
- const status = project.status === "ACTIVE" ? "\u2705" : project.status === "ARCHIVED" ? "\uD83D\uDCE6" : "\u23F8\uFE0F";
3067
- lines.push(`| ${status} | **${project.name}** | ${project.description ?? "-"} |`);
3068
- }
3069
- }
3070
- return {
3071
- mimeType: "text/markdown",
3072
- body: lines.join(`
3073
- `)
3074
- };
3075
- }
3076
- };
3077
- var saasDashboardMarkdownRenderer = {
3078
- target: "markdown",
3079
- render: async (desc, _ctx) => {
3080
- if (desc.source.type !== "component" || desc.source.componentKey !== "SaasDashboard") {
3081
- throw new Error("saasDashboardMarkdownRenderer: not SaasDashboard");
3082
- }
3083
- const [projectsData, subscription] = await Promise.all([
3084
- mockListProjectsHandler({ limit: 50 }),
3085
- mockGetSubscriptionHandler()
3086
- ]);
3087
- const projects = projectsData.projects ?? [];
3088
- const activeProjects = projects.filter((p) => p.status === "ACTIVE").length;
3089
- const archivedProjects = projects.filter((p) => p.status === "ARCHIVED").length;
3090
- const lines = [
3091
- "# SaaS Dashboard",
3092
- "",
3093
- "> Organization overview and usage summary",
3094
- "",
3095
- "## Summary",
3096
- "",
3097
- "| Metric | Value |",
3098
- "|--------|-------|",
3099
- `| Total Projects | ${projectsData.total} |`,
3100
- `| Active Projects | ${activeProjects} |`,
3101
- `| Archived Projects | ${archivedProjects} |`,
3102
- `| Subscription Plan | ${subscription.planName} |`,
3103
- `| Subscription Status | ${subscription.status} |`,
3104
- "",
3105
- "## Projects",
3106
- ""
3107
- ];
3108
- if (projects.length === 0) {
3109
- lines.push("_No projects yet._");
3110
- } else {
3111
- lines.push("| Status | Project | Description |");
3112
- lines.push("|--------|---------|-------------|");
3113
- for (const project of projects.slice(0, 10)) {
3114
- const status = project.status === "ACTIVE" ? "\u2705" : project.status === "ARCHIVED" ? "\uD83D\uDCE6" : "\u23F8\uFE0F";
3115
- lines.push(`| ${status} | **${project.name}** | ${project.description ?? "-"} |`);
3116
- }
3117
- if (projects.length > 10) {
3118
- lines.push(`| ... | ... | _${projectsData.total - 10} more projects_ |`);
3119
- }
3120
- }
3121
- lines.push("");
3122
- lines.push("## Subscription");
3123
- lines.push("");
3124
- lines.push(`- **Plan**: ${subscription.planName}`);
3125
- lines.push(`- **Status**: ${subscription.status}`);
3126
- if (subscription.currentPeriodEnd) {
3127
- lines.push(`- **Period End**: ${new Date(subscription.currentPeriodEnd).toLocaleDateString()}`);
3128
- }
3129
- return {
3130
- mimeType: "text/markdown",
3131
- body: lines.join(`
3132
- `)
3133
- };
3134
- }
3135
- };
3136
- var saasBillingMarkdownRenderer = {
3137
- target: "markdown",
3138
- render: async (desc, _ctx) => {
3139
- if (desc.source.type !== "component" || desc.source.componentKey !== "SubscriptionView") {
3140
- throw new Error("saasBillingMarkdownRenderer: not SubscriptionView");
3141
- }
3142
- const subscription = await mockGetSubscriptionHandler();
3143
- const lines = [
3144
- "# Billing & Subscription",
3145
- "",
3146
- "> Current subscription details and billing information",
3147
- "",
3148
- "## Subscription Details",
3149
- "",
3150
- "| Property | Value |",
3151
- "|----------|-------|",
3152
- `| Plan | ${subscription.planName} |`,
3153
- `| Status | ${subscription.status} |`,
3154
- `| ID | ${subscription.id} |`,
3155
- `| Period Start | ${new Date(subscription.currentPeriodStart).toLocaleDateString()} |`,
3156
- `| Period End | ${new Date(subscription.currentPeriodEnd).toLocaleDateString()} |`
3157
- ];
3158
- lines.push("");
3159
- lines.push("## Plan Limits");
3160
- lines.push("");
3161
- lines.push(`- **Projects**: ${subscription.limits.projects}`);
3162
- lines.push(`- **Users**: ${subscription.limits.users}`);
3163
- lines.push("");
3164
- lines.push("## Plan Features");
3165
- lines.push("");
3166
- if (subscription.planName.toLowerCase().includes("free")) {
3167
- lines.push("- \u2705 Up to 3 projects");
3168
- lines.push("- \u2705 Basic support");
3169
- lines.push("- \u274C Priority support");
3170
- lines.push("- \u274C Advanced analytics");
3171
- } else if (subscription.planName.toLowerCase().includes("pro")) {
3172
- lines.push("- \u2705 Unlimited projects");
3173
- lines.push("- \u2705 Priority support");
3174
- lines.push("- \u2705 Advanced analytics");
3175
- lines.push("- \u274C Custom integrations");
3176
- } else {
3177
- lines.push("- \u2705 Unlimited projects");
3178
- lines.push("- \u2705 Priority support");
3179
- lines.push("- \u2705 Advanced analytics");
3180
- lines.push("- \u2705 Custom integrations");
3181
- lines.push("- \u2705 Dedicated support");
3182
- }
3183
- return {
3184
- mimeType: "text/markdown",
3185
- body: lines.join(`
3186
- `)
3187
- };
3188
- }
3189
- };
3190
- // src/ui/overlays/demo-overlays.ts
3191
- var saasFreeUserOverlay = {
3192
- overlayId: "saas-boilerplate.free-tier",
3193
- version: "1.0.0",
3194
- description: "Shows limitations for free tier users",
3195
- appliesTo: {
3196
- feature: "saas-boilerplate",
3197
- tier: "free"
3198
- },
3199
- modifications: [
3200
- {
3201
- type: "setLimit",
3202
- field: "projects",
3203
- max: 3,
3204
- message: "Upgrade to create more projects"
3205
- },
3206
- { type: "hideField", field: "advancedSettings", reason: "Pro feature" },
3207
- {
3208
- type: "addBadge",
3209
- position: "header",
3210
- label: "Free Plan",
3211
- variant: "default"
3212
- }
3213
- ]
3214
- };
3215
- var saasDemoOverlay = {
3216
- overlayId: "saas-boilerplate.demo-user",
3217
- version: "1.0.0",
3218
- description: "Demo mode for SaaS boilerplate",
3219
- appliesTo: {
3220
- feature: "saas-boilerplate",
3221
- role: "demo"
3222
- },
3223
- modifications: [
3224
- {
3225
- type: "hideField",
3226
- field: "billingSection",
3227
- reason: "Demo users cannot access billing"
3228
- },
3229
- {
3230
- type: "hideField",
3231
- field: "deleteAccount",
3232
- reason: "Not available in demo"
3233
- },
3234
- {
3235
- type: "addBadge",
3236
- position: "header",
3237
- label: "Demo Mode",
3238
- variant: "warning"
3239
- }
3240
- ]
3241
- };
3242
- var saasOverlays = [
3243
- saasFreeUserOverlay,
3244
- saasDemoOverlay
3245
- ];
3246
3245
  // src/index.ts
3247
3246
  import { identityRbacSchemaContribution } from "@contractspec/lib.identity-rbac";
3248
3247
  import { jobsSchemaContribution } from "@contractspec/lib.jobs";