@contractspec/example.saas-boilerplate 3.7.5 → 3.7.7

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