@powerhousedao/contributor-billing 1.0.0-dev.5 → 1.0.0-dev.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.
package/dist/style.css CHANGED
@@ -6610,7 +6610,6 @@
6610
6610
  --color-red-500: hsl(5 80% 78%);
6611
6611
  --color-red-600: hsl(4 81% 74%);
6612
6612
  --color-red-800: hsl(5 81% 61%);
6613
- --color-amber-500: oklch(76.9% 0.188 70.08);
6614
6613
  --color-green-100: hsl(134 51% 89%);
6615
6614
  --color-green-500: hsl(136 53% 66%);
6616
6615
  --color-green-800: hsl(136 53% 49%);
@@ -6623,12 +6622,7 @@
6623
6622
  --color-blue-900: hsl(209 100% 50%);
6624
6623
  --color-indigo-600: oklch(51.1% 0.262 276.966);
6625
6624
  --color-indigo-700: oklch(45.7% 0.24 277.023);
6626
- --color-purple-100: hsl(262 79% 93%);
6627
- --color-purple-800: hsl(263 78% 66%);
6628
- --color-slate-300: hsl(210 9% 39%);
6629
6625
  --color-slate-600: hsl(210 9% 13%);
6630
- --color-slate-700: hsl(204 9% 11%);
6631
- --color-slate-900: hsl(195 6% 6%);
6632
6626
  --color-gray-50: hsl(0 0% 99%);
6633
6627
  --color-gray-100: hsl(0 0% 96%);
6634
6628
  --color-gray-200: hsl(0 0% 94%);
@@ -6640,6 +6634,7 @@
6640
6634
  --color-gray-900: hsl(192 5% 21%);
6641
6635
  --color-white: hsl(0 0% 100%);
6642
6636
  --spacing: 0.25rem;
6637
+ --container-5xl: 64rem;
6643
6638
  --container-6xl: 72rem;
6644
6639
  --container-7xl: 80rem;
6645
6640
  --text-xs: 0.75rem;
@@ -7010,6 +7005,9 @@
7010
7005
  .h-screen {
7011
7006
  height: 100vh;
7012
7007
  }
7008
+ .min-h-full {
7009
+ min-height: 100%;
7010
+ }
7013
7011
  .w-6 {
7014
7012
  width: calc(var(--spacing) * 6);
7015
7013
  }
@@ -7031,6 +7029,9 @@
7031
7029
  .w-full {
7032
7030
  width: 100%;
7033
7031
  }
7032
+ .max-w-5xl {
7033
+ max-width: var(--container-5xl);
7034
+ }
7034
7035
  .max-w-6xl {
7035
7036
  max-width: var(--container-6xl);
7036
7037
  }
@@ -7170,10 +7171,6 @@
7170
7171
  border-style: var(--tw-border-style);
7171
7172
  border-width: 0px;
7172
7173
  }
7173
- .border-2 {
7174
- border-style: var(--tw-border-style);
7175
- border-width: 2px;
7176
- }
7177
7174
  .border-t {
7178
7175
  border-top-style: var(--tw-border-style);
7179
7176
  border-top-width: 1px;
@@ -7195,9 +7192,6 @@
7195
7192
  .border-red-300 {
7196
7193
  border-color: var(--color-red-300);
7197
7194
  }
7198
- .border-slate-300 {
7199
- border-color: var(--color-slate-300);
7200
- }
7201
7195
  .border-white\/10 {
7202
7196
  border-color: color-mix(in srgb, hsl(0 0% 100%) 10%, transparent);
7203
7197
  @supports (color: color-mix(in lab, red, red)) {
@@ -7207,15 +7201,6 @@
7207
7201
  }
7208
7202
  }
7209
7203
  }
7210
- .bg-\[\#6B21A8\] {
7211
- background-color: #6B21A8;
7212
- }
7213
- .bg-\[\#0369A1\] {
7214
- background-color: #0369A1;
7215
- }
7216
- .bg-amber-500 {
7217
- background-color: var(--color-amber-500);
7218
- }
7219
7204
  .bg-blue-50 {
7220
7205
  background-color: var(--color-blue-50);
7221
7206
  }
@@ -7246,9 +7231,6 @@
7246
7231
  .bg-green-500 {
7247
7232
  background-color: var(--color-green-500);
7248
7233
  }
7249
- .bg-purple-100 {
7250
- background-color: var(--color-purple-100);
7251
- }
7252
7234
  .bg-red-50 {
7253
7235
  background-color: var(--color-red-50);
7254
7236
  }
@@ -7258,9 +7240,6 @@
7258
7240
  .bg-red-500 {
7259
7241
  background-color: var(--color-red-500);
7260
7242
  }
7261
- .bg-slate-900 {
7262
- background-color: var(--color-slate-900);
7263
- }
7264
7243
  .bg-transparent {
7265
7244
  background-color: transparent;
7266
7245
  }
@@ -7331,6 +7310,9 @@
7331
7310
  .py-4 {
7332
7311
  padding-block: calc(var(--spacing) * 4);
7333
7312
  }
7313
+ .py-6 {
7314
+ padding-block: calc(var(--spacing) * 6);
7315
+ }
7334
7316
  .py-8 {
7335
7317
  padding-block: calc(var(--spacing) * 8);
7336
7318
  }
@@ -7343,6 +7325,9 @@
7343
7325
  .text-center {
7344
7326
  text-align: center;
7345
7327
  }
7328
+ .text-right {
7329
+ text-align: right;
7330
+ }
7346
7331
  .font-mono {
7347
7332
  font-family: var(--font-mono);
7348
7333
  }
@@ -7393,9 +7378,6 @@
7393
7378
  .whitespace-pre-wrap {
7394
7379
  white-space: pre-wrap;
7395
7380
  }
7396
- .text-\[\#0369A1\] {
7397
- color: #0369A1;
7398
- }
7399
7381
  .text-blue-600 {
7400
7382
  color: var(--color-blue-600);
7401
7383
  }
@@ -7426,9 +7408,6 @@
7426
7408
  .text-indigo-600 {
7427
7409
  color: var(--color-indigo-600);
7428
7410
  }
7429
- .text-purple-800 {
7430
- color: var(--color-purple-800);
7431
- }
7432
7411
  .text-red-500 {
7433
7412
  color: var(--color-red-500);
7434
7413
  }
@@ -7441,20 +7420,14 @@
7441
7420
  .text-slate-600 {
7442
7421
  color: var(--color-slate-600);
7443
7422
  }
7444
- .text-slate-700 {
7445
- color: var(--color-slate-700);
7446
- }
7447
- .text-slate-900 {
7448
- color: var(--color-slate-900);
7449
- }
7450
7423
  .text-white {
7451
7424
  color: var(--color-white);
7452
7425
  }
7453
7426
  .lowercase {
7454
7427
  text-transform: lowercase;
7455
7428
  }
7456
- .uppercase {
7457
- text-transform: uppercase;
7429
+ .line-through {
7430
+ text-decoration-line: line-through;
7458
7431
  }
7459
7432
  .shadow {
7460
7433
  --tw-shadow: 0 1px 3px 0 var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 1px 2px -1px var(--tw-shadow-color, rgb(0 0 0 / 0.1));
@@ -7488,6 +7461,10 @@
7488
7461
  --tw-blur: blur(8px);
7489
7462
  filter: var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,);
7490
7463
  }
7464
+ .invert {
7465
+ --tw-invert: invert(100%);
7466
+ filter: var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,);
7467
+ }
7491
7468
  .sepia {
7492
7469
  --tw-sepia: sepia(100%);
7493
7470
  filter: var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,);
@@ -7514,20 +7491,6 @@
7514
7491
  transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));
7515
7492
  transition-duration: var(--tw-duration, var(--default-transition-duration));
7516
7493
  }
7517
- .transition-opacity {
7518
- transition-property: opacity;
7519
- transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));
7520
- transition-duration: var(--tw-duration, var(--default-transition-duration));
7521
- }
7522
- .transition-shadow {
7523
- transition-property: box-shadow;
7524
- transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));
7525
- transition-duration: var(--tw-duration, var(--default-transition-duration));
7526
- }
7527
- .duration-150 {
7528
- --tw-duration: 150ms;
7529
- transition-duration: 150ms;
7530
- }
7531
7494
  .duration-200 {
7532
7495
  --tw-duration: 200ms;
7533
7496
  transition-duration: 200ms;
@@ -7552,13 +7515,6 @@
7552
7515
  --tw-outline-style: none;
7553
7516
  outline-style: none;
7554
7517
  }
7555
- .hover\:border-\[\#0369A1\] {
7556
- &:hover {
7557
- @media (hover: hover) {
7558
- border-color: #0369A1;
7559
- }
7560
- }
7561
- }
7562
7518
  .hover\:border-gray-300 {
7563
7519
  &:hover {
7564
7520
  @media (hover: hover) {
@@ -7566,20 +7522,6 @@
7566
7522
  }
7567
7523
  }
7568
7524
  }
7569
- .hover\:bg-\[\#7C3AED\] {
7570
- &:hover {
7571
- @media (hover: hover) {
7572
- background-color: #7C3AED;
7573
- }
7574
- }
7575
- }
7576
- .hover\:bg-\[\#0284C7\] {
7577
- &:hover {
7578
- @media (hover: hover) {
7579
- background-color: #0284C7;
7580
- }
7581
- }
7582
- }
7583
7525
  .hover\:bg-blue-50 {
7584
7526
  &:hover {
7585
7527
  @media (hover: hover) {
@@ -7615,13 +7557,6 @@
7615
7557
  }
7616
7558
  }
7617
7559
  }
7618
- .hover\:text-\[\#0369A1\] {
7619
- &:hover {
7620
- @media (hover: hover) {
7621
- color: #0369A1;
7622
- }
7623
- }
7624
- }
7625
7560
  .hover\:text-indigo-700 {
7626
7561
  &:hover {
7627
7562
  @media (hover: hover) {
@@ -7636,14 +7571,6 @@
7636
7571
  }
7637
7572
  }
7638
7573
  }
7639
- .hover\:shadow-lg {
7640
- &:hover {
7641
- @media (hover: hover) {
7642
- --tw-shadow: 0 10px 15px -3px var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 4px 6px -4px var(--tw-shadow-color, rgb(0 0 0 / 0.1));
7643
- box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
7644
- }
7645
- }
7646
- }
7647
7574
  .focus\:border-transparent {
7648
7575
  &:focus {
7649
7576
  border-color: transparent;
@@ -7655,22 +7582,11 @@
7655
7582
  box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
7656
7583
  }
7657
7584
  }
7658
- .focus\:ring-\[\#0369A1\] {
7659
- &:focus {
7660
- --tw-ring-color: #0369A1;
7661
- }
7662
- }
7663
7585
  .focus\:ring-blue-500 {
7664
7586
  &:focus {
7665
7587
  --tw-ring-color: var(--color-blue-500);
7666
7588
  }
7667
7589
  }
7668
- .focus\:ring-offset-2 {
7669
- &:focus {
7670
- --tw-ring-offset-width: 2px;
7671
- --tw-ring-offset-shadow: var(--tw-ring-inset,) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
7672
- }
7673
- }
7674
7590
  .focus\:outline-none {
7675
7591
  &:focus {
7676
7592
  --tw-outline-style: none;
@@ -1,5 +1,4 @@
1
1
  export * as InvoiceAddonSubgraph from "./invoice-addon/index.js";
2
2
  export * as AccTxsAddonSubgraph from "./acc-txs-addon/index.js";
3
3
  export * as BudgetStatementsSubgraph from "./budget-statements/index.js";
4
- export * as ResourcesServicesSubgraph from "./resources-services/index.js";
5
4
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../subgraphs/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,oBAAoB,MAAM,0BAA0B,CAAC;AACjE,OAAO,KAAK,mBAAmB,MAAM,0BAA0B,CAAC;AAChE,OAAO,KAAK,wBAAwB,MAAM,8BAA8B,CAAC;AACzE,OAAO,KAAK,yBAAyB,MAAM,+BAA+B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../subgraphs/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,oBAAoB,MAAM,0BAA0B,CAAC;AACjE,OAAO,KAAK,mBAAmB,MAAM,0BAA0B,CAAC;AAChE,OAAO,KAAK,wBAAwB,MAAM,8BAA8B,CAAC"}
@@ -1,4 +1,3 @@
1
1
  export * as InvoiceAddonSubgraph from "./invoice-addon/index.js";
2
2
  export * as AccTxsAddonSubgraph from "./acc-txs-addon/index.js";
3
3
  export * as BudgetStatementsSubgraph from "./budget-statements/index.js";
4
- export * as ResourcesServicesSubgraph from "./resources-services/index.js";
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@powerhousedao/contributor-billing",
3
3
  "description": "Document models that help contributors of open organisations get paid anonymously for their work on a monthly basis.",
4
- "version": "1.0.0-dev.5",
4
+ "version": "1.0.0-dev.7",
5
5
  "license": "AGPL-3.0-only",
6
6
  "repository": {
7
7
  "type": "git",
@@ -74,7 +74,7 @@
74
74
  "@powerhousedao/common": "5.3.3",
75
75
  "@powerhousedao/design-system": "5.3.3",
76
76
  "@powerhousedao/document-engineering": "^1.40.1",
77
- "@powerhousedao/service-offering": "^0.0.1",
77
+ "@powerhousedao/service-offering": "0.0.3",
78
78
  "@powerhousedao/vetra": "^5.3.3",
79
79
  "@react-pdf/renderer": "^4.3.1",
80
80
  "@safe-global/api-kit": "^4.0.0",
@@ -1,11 +0,0 @@
1
- import { BaseSubgraph } from "@powerhousedao/reactor-api";
2
- import type { DocumentNode } from "graphql";
3
- export declare class ResourcesServicesSubgraph extends BaseSubgraph {
4
- name: string;
5
- typeDefs: DocumentNode;
6
- resolvers: Record<string, unknown>;
7
- additionalContextFields: {};
8
- onSetup(): Promise<void>;
9
- onDisconnect(): Promise<void>;
10
- }
11
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../subgraphs/resources-services/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC1D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAI5C,qBAAa,yBAA0B,SAAQ,YAAY;IACzD,IAAI,SAAwB;IAC5B,QAAQ,EAAE,YAAY,CAAU;IAChC,SAAS,0BAAsB;IAC/B,uBAAuB,KAAM;IACvB,OAAO;IACP,YAAY;CACnB"}
@@ -1,11 +0,0 @@
1
- import { BaseSubgraph } from "@powerhousedao/reactor-api";
2
- import { schema } from "./schema.js";
3
- import { getResolvers } from "./resolvers.js";
4
- export class ResourcesServicesSubgraph extends BaseSubgraph {
5
- name = "resources-services";
6
- typeDefs = schema;
7
- resolvers = getResolvers(this);
8
- additionalContextFields = {};
9
- async onSetup() { }
10
- async onDisconnect() { }
11
- }
@@ -1,3 +0,0 @@
1
- import { type ISubgraph } from "@powerhousedao/reactor-api";
2
- export declare const getResolvers: (subgraph: ISubgraph) => Record<string, unknown>;
3
- //# sourceMappingURL=resolvers.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"resolvers.d.ts","sourceRoot":"","sources":["../../../subgraphs/resources-services/resolvers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,4BAA4B,CAAC;AAkC5D,eAAO,MAAM,YAAY,GAAI,UAAU,SAAS,KAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAyUxE,CAAC"}
@@ -1,462 +0,0 @@
1
- import {} from "@powerhousedao/reactor-api";
2
- import { addFile } from "document-drive";
3
- import { BuilderProfile } from "@powerhousedao/builder-profile/document-models";
4
- import { ResourceInstance } from "@powerhousedao/service-offering/document-models";
5
- export const getResolvers = (subgraph) => {
6
- const reactor = subgraph.reactor;
7
- return {
8
- Query: {
9
- resourceTemplates: async (_, args) => {
10
- const { id, status, operatorId } = args.filter || {};
11
- // If filtering by specific id, try to fetch directly
12
- if (id) {
13
- try {
14
- const doc = await reactor.getDocument(id);
15
- if (doc &&
16
- doc.header.documentType === "powerhouse/resource-template") {
17
- const state = doc.state.global;
18
- // Check status filter if provided
19
- if (status &&
20
- status.length > 0 &&
21
- !status.includes(state.status)) {
22
- return [];
23
- }
24
- // Check operatorId filter if provided
25
- if (operatorId && state.operatorId !== operatorId) {
26
- return [];
27
- }
28
- return [mapResourceTemplateState(state, doc)];
29
- }
30
- }
31
- catch {
32
- // Document not found
33
- }
34
- return [];
35
- }
36
- // Scan all drives for resource template documents
37
- const drives = await reactor.getDrives();
38
- const resourceTemplates = [];
39
- for (const driveId of drives) {
40
- try {
41
- const docIds = await reactor.getDocuments(driveId);
42
- const docs = await Promise.all(docIds.map(async (docId) => {
43
- try {
44
- return await reactor.getDocument(docId);
45
- }
46
- catch {
47
- return null;
48
- }
49
- }));
50
- for (const doc of docs) {
51
- if (doc &&
52
- doc.header.documentType === "powerhouse/resource-template") {
53
- const resourceDoc = doc;
54
- const state = resourceDoc.state.global;
55
- // Apply status filter if provided
56
- if (status &&
57
- status.length > 0 &&
58
- !status.includes(state.status)) {
59
- continue;
60
- }
61
- // Apply operatorId filter if provided
62
- if (operatorId && state.operatorId !== operatorId) {
63
- continue;
64
- }
65
- resourceTemplates.push(mapResourceTemplateState(state, doc));
66
- }
67
- }
68
- }
69
- catch (error) {
70
- console.warn(`Failed to inspect drive ${driveId}:`, error);
71
- }
72
- }
73
- return resourceTemplates;
74
- },
75
- serviceOfferings: async (_, args) => {
76
- const { id, status, operatorId, resourceTemplateId } = args.filter || {};
77
- // If filtering by specific id, try to fetch directly
78
- if (id) {
79
- try {
80
- const doc = await reactor.getDocument(id);
81
- if (doc &&
82
- doc.header.documentType === "powerhouse/service-offering") {
83
- const state = doc.state.global;
84
- // Check status filter if provided
85
- if (status &&
86
- status.length > 0 &&
87
- !status.includes(state.status)) {
88
- return [];
89
- }
90
- // Check operatorId filter if provided
91
- if (operatorId && state.operatorId !== operatorId) {
92
- return [];
93
- }
94
- // Check resourceTemplateId filter if provided
95
- if (resourceTemplateId &&
96
- state.resourceTemplateId !== resourceTemplateId) {
97
- return [];
98
- }
99
- return [mapServiceOfferingState(state, doc)];
100
- }
101
- }
102
- catch {
103
- // Document not found
104
- }
105
- return [];
106
- }
107
- // Scan all drives for service offering documents
108
- const drives = await reactor.getDrives();
109
- const serviceOfferings = [];
110
- for (const driveId of drives) {
111
- try {
112
- const docIds = await reactor.getDocuments(driveId);
113
- const docs = await Promise.all(docIds.map(async (docId) => {
114
- try {
115
- return await reactor.getDocument(docId);
116
- }
117
- catch {
118
- return null;
119
- }
120
- }));
121
- for (const doc of docs) {
122
- if (doc &&
123
- doc.header.documentType === "powerhouse/service-offering") {
124
- const offeringDoc = doc;
125
- const state = offeringDoc.state.global;
126
- // Apply status filter if provided
127
- if (status &&
128
- status.length > 0 &&
129
- !status.includes(state.status)) {
130
- continue;
131
- }
132
- // Apply operatorId filter if provided
133
- if (operatorId && state.operatorId !== operatorId) {
134
- continue;
135
- }
136
- // Apply resourceTemplateId filter if provided
137
- if (resourceTemplateId &&
138
- state.resourceTemplateId !== resourceTemplateId) {
139
- continue;
140
- }
141
- serviceOfferings.push(mapServiceOfferingState(state, doc));
142
- }
143
- }
144
- }
145
- catch (error) {
146
- console.warn(`Failed to inspect drive ${driveId}:`, error);
147
- }
148
- }
149
- return serviceOfferings;
150
- },
151
- },
152
- Mutation: {
153
- createResourceInstances: async (_, args) => {
154
- const { input } = args;
155
- const { resourceTemplateId, name, teamName } = input;
156
- // Validate input
157
- if (!resourceTemplateId) {
158
- return {
159
- success: false,
160
- data: null,
161
- errors: ["Resource template ID is required"],
162
- };
163
- }
164
- if (!name) {
165
- return { success: false, data: null, errors: ["Name is required"] };
166
- }
167
- if (!teamName) {
168
- return {
169
- success: false,
170
- data: null,
171
- errors: ["Team name is required"],
172
- };
173
- }
174
- const parsedTeamName = teamName.toLowerCase().replace(/ /g, "-");
175
- const parsedName = name.toLowerCase().replace(/ /g, "-");
176
- try {
177
- // create team-builder-admin drive
178
- const teamBuilderAdminDrive = await reactor.addDrive({
179
- global: {
180
- name: teamName,
181
- icon: "https://cdn-icons-png.flaticon.com/512/6020/6020347.png",
182
- },
183
- id: parsedTeamName,
184
- slug: parsedTeamName,
185
- preferredEditor: "builder-team-admin",
186
- });
187
- teamBuilderAdminDrive.header.id;
188
- // create builder-profile doc inside the team-builder-admin drive
189
- const builderProfileDoc = await reactor.addDocument("powerhouse/builder-profile");
190
- await reactor.addAction(teamBuilderAdminDrive.header.id, addFile({
191
- documentType: "powerhouse/builder-profile",
192
- id: builderProfileDoc.header.id,
193
- name: `${parsedName} Builder Profile`,
194
- parentFolder: teamBuilderAdminDrive.state.global.nodes?.find((node) => node.kind === "folder")?.parentFolder,
195
- }));
196
- await reactor.addAction(builderProfileDoc.header.id, BuilderProfile.actions.updateProfile({
197
- name: name,
198
- }));
199
- // create resource-instance doc inside the team-builder-admin drive
200
- const resourceInstanceDoc = await reactor.addDocument("powerhouse/resource-instance");
201
- await reactor.addAction(teamBuilderAdminDrive.header.id, addFile({
202
- documentType: "powerhouse/resource-instance",
203
- id: resourceInstanceDoc.header.id,
204
- name: `${parsedName} Resource Instance`,
205
- parentFolder: teamBuilderAdminDrive.state.global.nodes?.find((node) => node.kind === "folder")?.parentFolder,
206
- }));
207
- await populateResourceInstance(reactor, resourceInstanceDoc.header.id, resourceTemplateId, builderProfileDoc.header.id, name);
208
- // create copy of resource-instance doc inside the operator's drive
209
- const operatorDrive = await getOperatorDrive(reactor, resourceTemplateId);
210
- if (!operatorDrive) {
211
- throw new Error(`Operator drive not found for resource template ${resourceTemplateId}`);
212
- }
213
- await reactor.addAction(operatorDrive.header.id, addFile({
214
- documentType: "powerhouse/resource-instance",
215
- id: resourceInstanceDoc.header.id,
216
- name: `${parsedName} Resource Instance`,
217
- parentFolder: operatorDrive.state.global.nodes?.find((node) => node.kind === "folder")?.parentFolder,
218
- }));
219
- return {
220
- success: true,
221
- data: {
222
- linkToDrive: getDriveLink(teamBuilderAdminDrive.header.id),
223
- },
224
- errors: [],
225
- };
226
- }
227
- catch (error) {
228
- console.error("Failed to create resource instance:", error);
229
- return {
230
- success: false,
231
- data: null,
232
- errors: [
233
- error instanceof Error
234
- ? error.message
235
- : "An unexpected error occurred",
236
- ],
237
- };
238
- }
239
- },
240
- },
241
- };
242
- };
243
- /**
244
- * Build a link to a drive based on the current environment.
245
- * Mirrors the logic from editors/shared/graphql.ts for server-side use.
246
- */
247
- function getDriveLink(driveId) {
248
- const baseUri = process.env.BASE_URI || "";
249
- if (baseUri.includes("-dev.")) {
250
- return `https://connect-dev.powerhouse.xyz/?driveUrl=https://switchboard-dev.powerhouse.xyz/d/${driveId}`;
251
- }
252
- if (baseUri.includes("-staging.")) {
253
- return `https://connect-staging.powerhouse.xyz/?driveUrl=https://switchboard-staging.powerhouse.xyz/d/${driveId}`;
254
- }
255
- if (baseUri && !baseUri.includes("localhost")) {
256
- return `https://connect.powerhouse.xyz/?driveUrl=https://switchboard.powerhouse.xyz/d/${driveId}`;
257
- }
258
- return `http://localhost:3000/?driveUrl=http://localhost:4001/d/${driveId}`;
259
- }
260
- /**
261
- * Map ResourceTemplateState from document model to GraphQL response
262
- */
263
- function mapResourceTemplateState(state, doc) {
264
- return {
265
- id: doc.header.id,
266
- operatorId: state.operatorId,
267
- title: state.title,
268
- summary: state.summary,
269
- description: state.description || null,
270
- thumbnailUrl: state.thumbnailUrl || null,
271
- infoLink: state.infoLink || null,
272
- status: state.status,
273
- lastModified: state.lastModified,
274
- targetAudiences: state.targetAudiences.map((audience) => ({
275
- id: audience.id,
276
- label: audience.label,
277
- color: audience.color || null,
278
- })),
279
- setupServices: state.setupServices,
280
- recurringServices: state.recurringServices,
281
- facetTargets: state.facetTargets.map((facet) => ({
282
- id: facet.id,
283
- categoryKey: facet.categoryKey,
284
- categoryLabel: facet.categoryLabel,
285
- selectedOptions: facet.selectedOptions,
286
- })),
287
- services: (state.services || []).map((service) => ({
288
- id: service.id,
289
- title: service.title,
290
- description: service.description || null,
291
- displayOrder: service.displayOrder ?? null,
292
- parentServiceId: service.parentServiceId || null,
293
- isSetupFormation: service.isSetupFormation,
294
- optionGroupId: service.optionGroupId || null,
295
- facetBindings: (service.facetBindings || []).map((binding) => ({
296
- id: binding.id,
297
- facetName: binding.facetName,
298
- facetType: binding.facetType,
299
- supportedOptions: binding.supportedOptions,
300
- })),
301
- })),
302
- optionGroups: (state.optionGroups || []).map((group) => ({
303
- id: group.id,
304
- name: group.name,
305
- description: group.description || null,
306
- isAddOn: group.isAddOn,
307
- defaultSelected: group.defaultSelected,
308
- })),
309
- faqFields: (state.faqFields || []).map((faq) => ({
310
- id: faq.id,
311
- question: faq.question || null,
312
- answer: faq.answer || null,
313
- displayOrder: faq.displayOrder,
314
- })),
315
- contentSections: state.contentSections.map((section) => ({
316
- id: section.id,
317
- title: section.title,
318
- content: section.content,
319
- displayOrder: section.displayOrder,
320
- })),
321
- };
322
- }
323
- /**
324
- * Populate a resource-instance document with data from a resource-template.
325
- * Initializes basic info and sets facet configuration from template facetTargets.
326
- */
327
- async function populateResourceInstance(reactor, resourceInstanceDocId, resourceTemplateId, profileId, name) {
328
- const resourceTemplateDoc = await reactor.getDocument(resourceTemplateId);
329
- if (!resourceTemplateDoc)
330
- return;
331
- const templateState = resourceTemplateDoc.state.global;
332
- // Initialize instance with basic info from template
333
- await reactor.addAction(resourceInstanceDocId, ResourceInstance.actions.initializeInstance({
334
- profileId,
335
- profileDocumentType: "powerhouse/builder-profile",
336
- resourceTemplateId,
337
- customerId: null,
338
- name,
339
- thumbnailUrl: templateState.thumbnailUrl,
340
- infoLink: templateState.infoLink,
341
- description: templateState.description,
342
- }));
343
- // Populate facet configuration from template's facetTargets
344
- for (const facetTarget of templateState.facetTargets) {
345
- if (facetTarget.selectedOptions.length > 0) {
346
- await reactor.addAction(resourceInstanceDocId, ResourceInstance.actions.setInstanceFacet({
347
- id: facetTarget.id,
348
- categoryKey: facetTarget.categoryKey,
349
- categoryLabel: facetTarget.categoryLabel,
350
- selectedOption: facetTarget.selectedOptions[0],
351
- }));
352
- }
353
- }
354
- }
355
- /**
356
- * Map ServiceOfferingState from document model to GraphQL response
357
- */
358
- function mapServiceOfferingState(state, doc) {
359
- return {
360
- id: doc.header.id,
361
- operatorId: state.operatorId,
362
- resourceTemplateId: state.resourceTemplateId || null,
363
- title: state.title,
364
- summary: state.summary,
365
- description: state.description || null,
366
- thumbnailUrl: state.thumbnailUrl || null,
367
- infoLink: state.infoLink || null,
368
- status: state.status,
369
- lastModified: state.lastModified,
370
- targetAudiences: state.targetAudiences.map((audience) => ({
371
- id: audience.id,
372
- label: audience.label,
373
- color: audience.color || null,
374
- })),
375
- facetTargets: state.facetTargets.map((facet) => ({
376
- id: facet.id,
377
- categoryKey: facet.categoryKey,
378
- categoryLabel: facet.categoryLabel,
379
- selectedOptions: facet.selectedOptions,
380
- })),
381
- serviceGroups: (state.serviceGroups || []).map((group) => ({
382
- id: group.id,
383
- name: group.name,
384
- description: group.description || null,
385
- billingCycle: group.billingCycle,
386
- displayOrder: group.displayOrder ?? null,
387
- })),
388
- services: state.services.map((service) => ({
389
- id: service.id,
390
- title: service.title,
391
- description: service.description || null,
392
- displayOrder: service.displayOrder ?? null,
393
- serviceGroupId: service.serviceGroupId || null,
394
- isSetupFormation: service.isSetupFormation,
395
- optionGroupId: service.optionGroupId || null,
396
- costType: service.costType || null,
397
- price: service.price ?? null,
398
- currency: service.currency || null,
399
- facetBindings: (service.facetBindings || []).map((binding) => ({
400
- id: binding.id,
401
- facetName: binding.facetName,
402
- facetType: binding.facetType,
403
- supportedOptions: binding.supportedOptions,
404
- })),
405
- })),
406
- tiers: state.tiers.map((tier) => ({
407
- id: tier.id,
408
- name: tier.name,
409
- description: tier.description || null,
410
- isCustomPricing: tier.isCustomPricing,
411
- pricing: {
412
- amount: tier.pricing.amount ?? null,
413
- currency: tier.pricing.currency,
414
- },
415
- pricingOptions: tier.pricingOptions.map((option) => ({
416
- id: option.id,
417
- amount: option.amount,
418
- currency: option.currency,
419
- isDefault: option.isDefault,
420
- })),
421
- serviceLevels: tier.serviceLevels.map((level) => ({
422
- id: level.id,
423
- serviceId: level.serviceId,
424
- level: level.level,
425
- customValue: level.customValue || null,
426
- optionGroupId: level.optionGroupId || null,
427
- })),
428
- usageLimits: tier.usageLimits.map((limit) => ({
429
- id: limit.id,
430
- serviceId: limit.serviceId,
431
- metric: limit.metric,
432
- unitName: limit.unitName || null,
433
- freeLimit: limit.freeLimit ?? null,
434
- paidLimit: limit.paidLimit ?? null,
435
- resetCycle: limit.resetCycle || null,
436
- notes: limit.notes || null,
437
- unitPrice: limit.unitPrice ?? null,
438
- unitPriceCurrency: limit.unitPriceCurrency || null,
439
- })),
440
- })),
441
- optionGroups: state.optionGroups.map((group) => ({
442
- id: group.id,
443
- name: group.name,
444
- description: group.description || null,
445
- isAddOn: group.isAddOn,
446
- defaultSelected: group.defaultSelected,
447
- costType: group.costType || null,
448
- billingCycle: group.billingCycle || null,
449
- price: group.price ?? null,
450
- currency: group.currency || null,
451
- })),
452
- };
453
- }
454
- async function getOperatorDrive(reactor, resourceTemplateId) {
455
- const drives = await reactor.getDrives();
456
- const results = await Promise.all(drives.map(async (drive) => {
457
- const docIds = await reactor.getDocuments(drive);
458
- return docIds.includes(resourceTemplateId) ? drive : null;
459
- }));
460
- const driveId = results.find((id) => id !== null);
461
- return driveId ? reactor.getDrive(driveId) : undefined;
462
- }
@@ -1,3 +0,0 @@
1
- import type { DocumentNode } from "graphql";
2
- export declare const schema: DocumentNode;
3
- //# sourceMappingURL=schema.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../../subgraphs/resources-services/schema.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAE5C,eAAO,MAAM,MAAM,EAAE,YA0RpB,CAAC"}
@@ -1,284 +0,0 @@
1
- import { gql } from "graphql-tag";
2
- export const schema = gql `
3
- """
4
- Subgraph definition for Resource Templates and Service Offerings
5
- """
6
- type Query {
7
- resourceTemplates(filter: RSResourceTemplatesFilter): [RSResourceTemplate!]!
8
- serviceOfferings(filter: RSServiceOfferingsFilter): [RSServiceOffering!]!
9
- }
10
-
11
- type Mutation {
12
- createResourceInstances(
13
- input: CreateResourceInstancesInput!
14
- ): CreateResourceInstancesOutput
15
- }
16
-
17
- input CreateResourceInstancesInput {
18
- resourceTemplateId: PHID!
19
- name: String!
20
- teamName: String!
21
- }
22
-
23
- type CreateResourceInstancesOutput {
24
- success: Boolean!
25
- data: JSONObject
26
- errors: [String!]!
27
- }
28
-
29
- # ============ Filters ============
30
-
31
- input RSResourceTemplatesFilter {
32
- id: PHID
33
- status: [RSTemplateStatusInput!]
34
- operatorId: PHID
35
- }
36
-
37
- input RSServiceOfferingsFilter {
38
- id: PHID
39
- status: [RSServiceStatus!]
40
- operatorId: PHID
41
- resourceTemplateId: PHID
42
- }
43
-
44
- # ============ Resource Template Types ============
45
-
46
- enum RSTemplateStatusInput {
47
- DRAFT
48
- COMING_SOON
49
- ACTIVE
50
- DEPRECATED
51
- }
52
-
53
- type RSResourceTemplate {
54
- id: PHID!
55
- operatorId: PHID!
56
- title: String!
57
- summary: String!
58
- description: String
59
- thumbnailUrl: URL
60
- infoLink: URL
61
- status: RSTemplateStatus!
62
- lastModified: DateTime!
63
- targetAudiences: [RSTargetAudience!]!
64
- setupServices: [String!]!
65
- recurringServices: [String!]!
66
- facetTargets: [RSFacetTarget!]!
67
- services: [RSService!]!
68
- optionGroups: [RSOptionGroup!]!
69
- faqFields: [RSFaqField!]
70
- contentSections: [RSContentSection!]!
71
- }
72
-
73
- enum RSTemplateStatus {
74
- DRAFT
75
- COMING_SOON
76
- ACTIVE
77
- DEPRECATED
78
- }
79
-
80
- type RSTargetAudience {
81
- id: OID!
82
- label: String!
83
- color: String
84
- }
85
-
86
- type RSOfferingFacetTarget {
87
- id: OID!
88
- categoryKey: String!
89
- categoryLabel: String!
90
- selectedOptions: [String!]!
91
- }
92
-
93
- type RSService {
94
- id: OID!
95
- title: String!
96
- description: String
97
- displayOrder: Int
98
- parentServiceId: OID
99
- isSetupFormation: Boolean!
100
- optionGroupId: OID
101
- facetBindings: [RSResourceFacetBinding!]!
102
- }
103
-
104
- type RSOptionGroup {
105
- id: OID!
106
- name: String!
107
- description: String
108
- isAddOn: Boolean!
109
- defaultSelected: Boolean!
110
- }
111
-
112
- type RSFaqField {
113
- id: OID!
114
- question: String
115
- answer: String
116
- displayOrder: Int!
117
- }
118
-
119
- type RSContentSection {
120
- id: OID!
121
- title: String!
122
- content: String!
123
- displayOrder: Int!
124
- }
125
-
126
- # ============ Service Offering Types ============
127
-
128
- type RSServiceOffering {
129
- id: PHID!
130
- operatorId: PHID!
131
- resourceTemplateId: PHID
132
- title: String!
133
- summary: String!
134
- description: String
135
- thumbnailUrl: URL
136
- infoLink: URL
137
- status: RSServiceStatus!
138
- lastModified: DateTime!
139
- targetAudiences: [RSOfferingTargetAudience!]!
140
- facetTargets: [RSOfferingFacetTarget!]!
141
- serviceGroups: [RSServiceGroup!]!
142
- services: [RSOfferingService!]!
143
- tiers: [RSServiceSubscriptionTier!]!
144
- optionGroups: [RSOfferingOptionGroup!]!
145
- }
146
-
147
- enum RSServiceStatus {
148
- DRAFT
149
- COMING_SOON
150
- ACTIVE
151
- DEPRECATED
152
- }
153
-
154
- type RSOfferingTargetAudience {
155
- id: OID!
156
- label: String!
157
- color: String
158
- }
159
-
160
- type RSFacetTarget {
161
- id: OID!
162
- categoryKey: String!
163
- categoryLabel: String!
164
- selectedOptions: [String!]!
165
- }
166
-
167
- type RSServiceGroup {
168
- id: OID!
169
- name: String!
170
- description: String
171
- billingCycle: RSBillingCycle!
172
- displayOrder: Int
173
- }
174
-
175
- type RSOfferingService {
176
- id: OID!
177
- title: String!
178
- description: String
179
- displayOrder: Int
180
- serviceGroupId: OID
181
- isSetupFormation: Boolean!
182
- optionGroupId: OID
183
- costType: RSServiceCostType
184
- price: Amount_Money
185
- currency: Currency
186
- facetBindings: [RSResourceFacetBinding!]!
187
- }
188
-
189
- enum RSServiceCostType {
190
- RECURRING
191
- SETUP
192
- }
193
-
194
- type RSResourceFacetBinding {
195
- id: OID!
196
- facetName: String!
197
- facetType: PHID!
198
- supportedOptions: [OID!]!
199
- }
200
-
201
- type RSServiceSubscriptionTier {
202
- id: OID!
203
- name: String!
204
- description: String
205
- isCustomPricing: Boolean!
206
- pricing: RSServicePricing!
207
- pricingOptions: [RSTierPricingOption!]!
208
- serviceLevels: [RSServiceLevelBinding!]!
209
- usageLimits: [RSServiceUsageLimit!]!
210
- }
211
-
212
- type RSServicePricing {
213
- amount: Amount_Money
214
- currency: Currency!
215
- }
216
-
217
- type RSTierPricingOption {
218
- id: OID!
219
- amount: Amount_Money!
220
- currency: Currency!
221
- isDefault: Boolean!
222
- }
223
-
224
- enum RSBillingCycle {
225
- MONTHLY
226
- QUARTERLY
227
- SEMI_ANNUAL
228
- ANNUAL
229
- ONE_TIME
230
- }
231
-
232
- type RSServiceLevelBinding {
233
- id: OID!
234
- serviceId: OID!
235
- level: RSServiceLevel!
236
- customValue: String
237
- optionGroupId: OID
238
- }
239
-
240
- enum RSServiceLevel {
241
- INCLUDED
242
- NOT_INCLUDED
243
- OPTIONAL
244
- CUSTOM
245
- VARIABLE
246
- NOT_APPLICABLE
247
- }
248
-
249
- type RSServiceUsageLimit {
250
- id: OID!
251
- serviceId: OID!
252
- metric: String!
253
- unitName: String
254
- freeLimit: Int
255
- paidLimit: Int
256
- resetCycle: RSUsageResetCycle
257
- notes: String
258
- unitPrice: Amount_Money
259
- unitPriceCurrency: Currency
260
- }
261
-
262
- enum RSUsageResetCycle {
263
- DAILY
264
- WEEKLY
265
- MONTHLY
266
- }
267
-
268
- type RSOfferingOptionGroup {
269
- id: OID!
270
- name: String!
271
- description: String
272
- isAddOn: Boolean!
273
- defaultSelected: Boolean!
274
- costType: RSGroupCostType
275
- billingCycle: RSBillingCycle
276
- price: Amount_Money
277
- currency: Currency
278
- }
279
-
280
- enum RSGroupCostType {
281
- RECURRING
282
- SETUP
283
- }
284
- `;