@saasicat/ui-vue 0.26.0 → 0.27.0

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 (36) hide show
  1. package/LICENSE +90 -201
  2. package/README.md +23 -0
  3. package/dist/{catalog-Dch1Ryw0.d.cts → catalog-Dv2twuNi.d.cts} +205 -4
  4. package/dist/{catalog-Dch1Ryw0.d.ts → catalog-Dv2twuNi.d.ts} +205 -4
  5. package/dist/{chunk-BPF2BMCQ.js → chunk-4IZWT4UY.js} +414 -2
  6. package/dist/{chunk-O3J3ITF2.js → chunk-LK6YJDXC.js} +1 -1
  7. package/dist/{chunk-NEXTZZRQ.js → chunk-NRVF3JJJ.js} +2 -2
  8. package/dist/client/index.cjs +426 -5
  9. package/dist/client/index.d.cts +70 -4
  10. package/dist/client/index.d.ts +70 -4
  11. package/dist/client/index.js +22 -4
  12. package/dist/index.cjs +429 -8
  13. package/dist/index.d.cts +5 -5
  14. package/dist/index.d.ts +5 -5
  15. package/dist/index.js +23 -5
  16. package/dist/quasar/index.cjs +411 -2
  17. package/dist/quasar/index.d.cts +2 -2
  18. package/dist/quasar/index.d.ts +2 -2
  19. package/dist/quasar/index.js +2 -2
  20. package/dist/{resource-registry-D7Xjs-5f.d.cts → resource-registry-BxFpnzHY.d.cts} +1 -1
  21. package/dist/{resource-registry-TaR7rtq0.d.ts → resource-registry-Cn1Gy2LE.d.ts} +1 -1
  22. package/package.json +5 -4
  23. package/src/client/admin-error.ts +18 -1
  24. package/src/client/attach-cause.ts +21 -0
  25. package/src/client/nav-builder.ts +3 -1
  26. package/src/client/resources/bundles.resource.ts +145 -0
  27. package/src/client/resources/catalog.resource.ts +160 -0
  28. package/src/client/resources/discovery.resource.ts +108 -0
  29. package/src/client/resources/index.ts +52 -14
  30. package/src/client/resources/marketing.resource.ts +121 -0
  31. package/src/client/resources/promo-codes.resource.ts +63 -0
  32. package/src/client/resources/promotions.resource.ts +44 -0
  33. package/src/client/resources/subscriptions.resource.ts +19 -0
  34. package/src/client/resources/tenants.resource.ts +35 -9
  35. package/src/client/resources/users.resource.ts +31 -0
  36. package/src/pages-standard/SuperAdminSetupWizard.vue +7 -4
@@ -4,6 +4,16 @@ function formatMessage(template, params) {
4
4
  return formatErrorMessage(template, params);
5
5
  }
6
6
 
7
+ // src/client/attach-cause.ts
8
+ function attachCause(error, cause) {
9
+ Object.defineProperty(error, "cause", {
10
+ value: cause,
11
+ configurable: true,
12
+ writable: true
13
+ });
14
+ return error;
15
+ }
16
+
7
17
  // src/client/admin-error.ts
8
18
  var ADMIN_ERROR = /* @__PURE__ */ Symbol.for("@saasicat/ui-vue/AdminError");
9
19
  var PLATFORM_ERROR = /* @__PURE__ */ Symbol.for("@saasicat/ui-vue/PlatformError");
@@ -63,7 +73,8 @@ var AdminError = class extends Error {
63
73
  /** The request never reached the server. See `AdminErrorInit`. */
64
74
  transportFailure;
65
75
  constructor(init = {}) {
66
- super(init.message ?? describe(init), { cause: init.cause });
76
+ super(init.message ?? describe(init));
77
+ if (init.cause !== void 0) attachCause(this, init.cause);
67
78
  this.name = "AdminError";
68
79
  this.status = init.status ?? 0;
69
80
  this.code = init.code;
@@ -536,9 +547,392 @@ var planVersionsResource = defineResource("planVersions", {
536
547
  )
537
548
  });
538
549
 
550
+ // src/client/resources/bundles.resource.ts
551
+ function bundlesUrl(ctx) {
552
+ return `${ctx.apiBase}/catalog/bundles`;
553
+ }
554
+ function bundleVersionsUrl(ctx) {
555
+ return `${ctx.apiBase}/catalog/bundle-versions`;
556
+ }
557
+ function scoped2(url, ctx) {
558
+ return `${url}?projectKey=${encodeURIComponent(ctx.projectKey)}`;
559
+ }
560
+ var bundlesResource = defineResource(
561
+ "bundles",
562
+ {
563
+ list: async (http, ctx) => await requestJson(http, scoped2(bundlesUrl(ctx), ctx)) ?? [],
564
+ create: (http, ctx, data) => requestJsonBody(http, bundlesUrl(ctx), "Create returned no body", {
565
+ method: "POST",
566
+ body: { ...data, projectKey: ctx.projectKey }
567
+ }),
568
+ update: (http, ctx, bundleId, data) => requestJsonBody(
569
+ http,
570
+ `${bundlesUrl(ctx)}/${bundleId}`,
571
+ "Update returned no body",
572
+ { method: "PATCH", body: data }
573
+ ),
574
+ /** Marks the bundle deleted. */
575
+ softDelete: async (http, ctx, bundleId) => {
576
+ await requestJson(http, `${bundlesUrl(ctx)}/${bundleId}`, { method: "DELETE" });
577
+ }
578
+ },
579
+ { projectScoped: true }
580
+ );
581
+ var bundleVersionsResource = defineResource("bundleVersions", {
582
+ listForBundle: async (http, ctx, bundleId) => await requestJson(http, `${bundlesUrl(ctx)}/${bundleId}/versions`) ?? [],
583
+ createDraft: (http, ctx, bundleId, data) => requestJsonBody(
584
+ http,
585
+ `${bundlesUrl(ctx)}/${bundleId}/versions`,
586
+ "CreateDraft returned no body",
587
+ { method: "POST", body: data }
588
+ ),
589
+ updateDraft: (http, ctx, versionId, data) => requestJsonBody(
590
+ http,
591
+ `${bundleVersionsUrl(ctx)}/${versionId}`,
592
+ "UpdateDraft returned no body",
593
+ { method: "PATCH", body: data }
594
+ ),
595
+ publish: (http, ctx, versionId, options = {}) => requestJsonBody(
596
+ http,
597
+ `${bundleVersionsUrl(ctx)}/${versionId}/publish`,
598
+ "Publish returned no body",
599
+ { method: "POST", body: options }
600
+ ),
601
+ /**
602
+ * Discards a draft. A published version cannot be discarded — the API
603
+ * answers 422 `BUNDLE_VERSION_ALREADY_PUBLISHED`.
604
+ */
605
+ discardDraft: async (http, ctx, versionId) => {
606
+ await requestJson(http, `${bundleVersionsUrl(ctx)}/${versionId}`, { method: "DELETE" });
607
+ }
608
+ });
609
+
610
+ // src/client/resources/catalog.resource.ts
611
+ function catalogUrl(ctx) {
612
+ return `${ctx.apiBase}/catalog`;
613
+ }
614
+ function scoped3(url, ctx) {
615
+ return `${url}?projectKey=${encodeURIComponent(ctx.projectKey)}`;
616
+ }
617
+ var catalogResource = defineResource(
618
+ "catalog",
619
+ {
620
+ capabilities: async (http, ctx) => await requestJson(
621
+ http,
622
+ scoped3(`${catalogUrl(ctx)}/capabilities`, ctx)
623
+ ) ?? [],
624
+ features: async (http, ctx) => await requestJson(
625
+ http,
626
+ scoped3(`${catalogUrl(ctx)}/features`, ctx)
627
+ ) ?? [],
628
+ quotas: async (http, ctx) => await requestJson(
629
+ http,
630
+ scoped3(`${catalogUrl(ctx)}/quotas`, ctx)
631
+ ) ?? [],
632
+ reviewFeature: (http, ctx, featureKey, data) => requestJsonBody(
633
+ http,
634
+ scoped3(`${catalogUrl(ctx)}/features/${encodeURIComponent(featureKey)}/review`, ctx),
635
+ "Review returned no body",
636
+ { method: "PATCH", body: data }
637
+ ),
638
+ reviewQuota: (http, ctx, quotaKey, data) => requestJsonBody(
639
+ http,
640
+ scoped3(`${catalogUrl(ctx)}/quotas/${encodeURIComponent(quotaKey)}/review`, ctx),
641
+ "Review returned no body",
642
+ { method: "PATCH", body: data }
643
+ ),
644
+ /** The translations are wrapped in `{ i18n }` here, not by the caller. */
645
+ setFeatureI18n: (http, ctx, featureKey, i18n) => requestJsonBody(
646
+ http,
647
+ scoped3(`${catalogUrl(ctx)}/features/${encodeURIComponent(featureKey)}/i18n`, ctx),
648
+ "i18n returned no body",
649
+ { method: "PATCH", body: { i18n } }
650
+ ),
651
+ setQuotaI18n: (http, ctx, quotaKey, i18n) => requestJsonBody(
652
+ http,
653
+ scoped3(`${catalogUrl(ctx)}/quotas/${encodeURIComponent(quotaKey)}/i18n`, ctx),
654
+ "i18n returned no body",
655
+ { method: "PATCH", body: { i18n } }
656
+ ),
657
+ setFeatureBase: (http, ctx, featureKey, data) => requestJsonBody(
658
+ http,
659
+ scoped3(`${catalogUrl(ctx)}/features/${encodeURIComponent(featureKey)}`, ctx),
660
+ "Base returned no body",
661
+ { method: "PATCH", body: data }
662
+ ),
663
+ setQuotaBase: (http, ctx, quotaKey, data) => requestJsonBody(
664
+ http,
665
+ scoped3(`${catalogUrl(ctx)}/quotas/${encodeURIComponent(quotaKey)}`, ctx),
666
+ "Base returned no body",
667
+ { method: "PATCH", body: data }
668
+ ),
669
+ /**
670
+ * Writes a discovery snapshot into the catalogue.
671
+ *
672
+ * The request only. `useCatalogEntries.syncDiscovery` reloads the three
673
+ * lists afterwards, which is that composable keeping its refs true —
674
+ * not part of the call, and not something an override should inherit
675
+ * invisibly.
676
+ */
677
+ syncDiscovery: (http, ctx, snapshot) => requestJsonBody(
678
+ http,
679
+ `${catalogUrl(ctx)}/discovery/sync`,
680
+ "Sync returned no body",
681
+ { method: "POST", body: { snapshot } }
682
+ )
683
+ },
684
+ { projectScoped: true }
685
+ );
686
+
687
+ // src/client/resources/discovery.resource.ts
688
+ function discoveryUrl(ctx) {
689
+ return `${ctx.apiBase}/discovery`;
690
+ }
691
+ function fail(status, method, url, message) {
692
+ return new AdminError({ status, url, method, message });
693
+ }
694
+ var discoveryResource = defineResource("discovery", {
695
+ /**
696
+ * Reads the snapshot, revalidating against `etag` when one is passed.
697
+ *
698
+ * Pass `null` (or nothing) to force a full read — that is what
699
+ * `useDiscovery.reload()` does when the operator asks for fresh data.
700
+ */
701
+ read: async (http, ctx, etag = null) => {
702
+ const url = discoveryUrl(ctx);
703
+ const headers = {};
704
+ if (etag) headers["If-None-Match"] = etag;
705
+ const response = await http(url, { method: "GET", headers });
706
+ requireServerAnswer(
707
+ response.status,
708
+ "GET",
709
+ url,
710
+ (diagnostic) => fail(response.status, "GET", url, diagnostic)
711
+ );
712
+ if (response.status === 304) return { status: "unchanged" };
713
+ if (response.status !== 200) {
714
+ throw fail(
715
+ response.status,
716
+ "GET",
717
+ url,
718
+ `Discovery endpoint responded with HTTP ${response.status}`
719
+ );
720
+ }
721
+ return {
722
+ status: "loaded",
723
+ snapshot: await response.json(),
724
+ etag: response.headers.get("ETag")
725
+ };
726
+ },
727
+ /**
728
+ * Re-runs the scan and returns what it found.
729
+ *
730
+ * Unconditional by construction: a rescan asks the server to look again,
731
+ * so revalidating against a tag the previous scan produced would answer
732
+ * with the snapshot the rescan was meant to replace.
733
+ */
734
+ rescan: async (http, ctx) => {
735
+ const url = `${discoveryUrl(ctx)}/rescan`;
736
+ const response = await http(url, {
737
+ method: "POST",
738
+ headers: { "content-type": "application/json" }
739
+ });
740
+ requireServerAnswer(
741
+ response.status,
742
+ "POST",
743
+ url,
744
+ (diagnostic) => fail(response.status, "POST", url, diagnostic)
745
+ );
746
+ if (response.status !== 200 && response.status !== 201) {
747
+ throw fail(
748
+ response.status,
749
+ "POST",
750
+ url,
751
+ `Discovery rescan responded with HTTP ${response.status}`
752
+ );
753
+ }
754
+ return {
755
+ snapshot: await response.json(),
756
+ etag: response.headers.get("ETag")
757
+ };
758
+ }
759
+ });
760
+
761
+ // src/client/resources/marketing.resource.ts
762
+ function projectionsUrl(ctx) {
763
+ return `${ctx.apiBase}/catalog/marketing-projections`;
764
+ }
765
+ function settingsUrl(ctx) {
766
+ return `${ctx.apiBase}/catalog/marketing-settings`;
767
+ }
768
+ var marketingResource = defineResource(
769
+ "marketing",
770
+ {
771
+ listProjections: async (http, ctx, filter = {}) => {
772
+ const params = new URLSearchParams();
773
+ params.set("projectKey", ctx.projectKey);
774
+ if (filter.targetType) params.set("targetType", filter.targetType);
775
+ if (filter.targetVersionId) params.set("targetVersionId", filter.targetVersionId);
776
+ if (filter.locale) params.set("locale", filter.locale);
777
+ return await requestJson(
778
+ http,
779
+ `${projectionsUrl(ctx)}?${params.toString()}`
780
+ ) ?? [];
781
+ },
782
+ /**
783
+ * The request only. `useMarketingProjections.create` reloads the list
784
+ * afterwards because a unique-tuple insert can change what the filter
785
+ * matches — that is the composable keeping its refs true, not part of
786
+ * the call.
787
+ */
788
+ createProjection: (http, ctx, data) => requestJsonBody(
789
+ http,
790
+ projectionsUrl(ctx),
791
+ "Create returned no body",
792
+ { method: "POST", body: data }
793
+ ),
794
+ updateProjection: (http, ctx, id, data) => requestJsonBody(
795
+ http,
796
+ `${projectionsUrl(ctx)}/${id}`,
797
+ "Update returned no body",
798
+ { method: "PATCH", body: data }
799
+ ),
800
+ deleteProjection: async (http, ctx, id) => {
801
+ await requestJson(http, `${projectionsUrl(ctx)}/${id}`, { method: "DELETE" });
802
+ },
803
+ /**
804
+ * The activated locale subset, or `null` when none was ever stored.
805
+ *
806
+ * `null` means the full pool is active — the server's answer for "no
807
+ * row", and the state the page falls back to.
808
+ */
809
+ settings: (http, ctx) => requestJson(
810
+ http,
811
+ `${settingsUrl(ctx)}?projectKey=${encodeURIComponent(ctx.projectKey)}`
812
+ ),
813
+ /** `PUT`, not `PATCH`: the endpoint replaces the set it is given. */
814
+ saveSettings: async (http, ctx, activeLocales) => {
815
+ await requestJson(http, settingsUrl(ctx), {
816
+ method: "PUT",
817
+ body: { projectKey: ctx.projectKey, activeLocales }
818
+ });
819
+ }
820
+ },
821
+ { projectScoped: true }
822
+ );
823
+
824
+ // src/client/resources/promo-codes.resource.ts
825
+ function promoCodesUrl(ctx) {
826
+ return `${ctx.apiBase}/promo-codes`;
827
+ }
828
+ var promoCodesResource = defineResource("promoCodes", {
829
+ list: async (http, ctx, filter = {}) => await requestJson(
830
+ http,
831
+ `${promoCodesUrl(ctx)}${filterQueryString({
832
+ search: filter.search,
833
+ status: filter.status
834
+ })}`
835
+ ) ?? [],
836
+ /**
837
+ * Returns nothing, matching `createAdminResourceClient.createPromo`.
838
+ *
839
+ * The endpoint does answer with the created record, but the page it feeds
840
+ * reloads the list rather than reading it, and promising a body a caller
841
+ * would then have to trust is a wider contract than anything holds today.
842
+ */
843
+ create: async (http, ctx, payload) => {
844
+ await requestJson(http, promoCodesUrl(ctx), { method: "POST", body: payload });
845
+ },
846
+ update: async (http, ctx, id, payload) => {
847
+ await requestJson(http, `${promoCodesUrl(ctx)}/${encodeURIComponent(id)}`, {
848
+ method: "PATCH",
849
+ body: payload
850
+ });
851
+ },
852
+ remove: async (http, ctx, id) => {
853
+ await requestJson(http, `${promoCodesUrl(ctx)}/${encodeURIComponent(id)}`, {
854
+ method: "DELETE"
855
+ });
856
+ }
857
+ });
858
+
859
+ // src/client/resources/promotions.resource.ts
860
+ function promotionsUrl(ctx) {
861
+ return `${ctx.apiBase}/catalog/promotions`;
862
+ }
863
+ var promotionsResource = defineResource(
864
+ "promotions",
865
+ {
866
+ list: async (http, ctx) => await requestJson(
867
+ http,
868
+ `${promotionsUrl(ctx)}?projectKey=${encodeURIComponent(ctx.projectKey)}`
869
+ ) ?? [],
870
+ create: (http, ctx, data) => requestJsonBody(http, promotionsUrl(ctx), "Create returned no body", {
871
+ method: "POST",
872
+ body: data
873
+ }),
874
+ update: (http, ctx, id, data) => requestJsonBody(
875
+ http,
876
+ `${promotionsUrl(ctx)}/${id}`,
877
+ "Update returned no body",
878
+ { method: "PATCH", body: data }
879
+ ),
880
+ remove: async (http, ctx, id) => {
881
+ await requestJson(http, `${promotionsUrl(ctx)}/${id}`, { method: "DELETE" });
882
+ }
883
+ },
884
+ { projectScoped: true }
885
+ );
886
+
539
887
  // src/client/resources/tenants.resource.ts
888
+ function tenantsUrl(ctx) {
889
+ return `${ctx.apiBase}/tenants`;
890
+ }
891
+ function tenantUrl(ctx, slug) {
892
+ return `${tenantsUrl(ctx)}/${encodeURIComponent(slug)}`;
893
+ }
540
894
  var tenantsResource = defineResource("tenants", {
541
- list: defineListOp((ctx) => `${ctx.apiBase}/tenants`)
895
+ list: defineListOp((ctx) => tenantsUrl(ctx)),
896
+ detail: async (http, ctx, slug) => requestJson(http, tenantUrl(ctx, slug)),
897
+ /**
898
+ * Suspends a tenant. The reason is recorded on the audit trail, so it is a
899
+ * required argument rather than an option with a default.
900
+ */
901
+ suspend: async (http, ctx, slug, reason) => {
902
+ await requestJson(http, `${tenantUrl(ctx, slug)}/suspend`, {
903
+ method: "POST",
904
+ body: { reason }
905
+ });
906
+ },
907
+ reactivate: async (http, ctx, slug) => {
908
+ await requestJson(http, `${tenantUrl(ctx, slug)}/reactivate`, { method: "POST" });
909
+ }
910
+ });
911
+
912
+ // src/client/resources/users.resource.ts
913
+ function usersUrl(ctx) {
914
+ return `${ctx.apiBase}/users`;
915
+ }
916
+ var usersResource = defineResource("users", {
917
+ /**
918
+ * `q` searches name and mail, `tenant` narrows to one tenant's members.
919
+ *
920
+ * Both are dropped when empty rather than sent as `q=` — see
921
+ * `isSentInQuery`; an empty search that reached the server would filter
922
+ * every row away on an endpoint that treats the parameter as a substring.
923
+ */
924
+ list: async (http, ctx, filter = {}) => await requestJson(
925
+ http,
926
+ `${usersUrl(ctx)}${filterQueryString({ q: filter.q, tenant: filter.tenant })}`
927
+ ) ?? []
928
+ });
929
+
930
+ // src/client/resources/subscriptions.resource.ts
931
+ function subscriptionsUrl(ctx) {
932
+ return `${ctx.apiBase}/subscriptions`;
933
+ }
934
+ var subscriptionsResource = defineResource("subscriptions", {
935
+ list: async (http, ctx) => await requestJson(http, subscriptionsUrl(ctx)) ?? []
542
936
  });
543
937
 
544
938
  // src/client/resources/audit.resource.ts
@@ -550,7 +944,16 @@ var auditResource = defineResource("audit", {
550
944
  var platformResources = {
551
945
  plans: plansResource,
552
946
  planVersions: planVersionsResource,
947
+ bundles: bundlesResource,
948
+ bundleVersions: bundleVersionsResource,
949
+ catalog: catalogResource,
950
+ discovery: discoveryResource,
951
+ marketing: marketingResource,
952
+ promoCodes: promoCodesResource,
953
+ promotions: promotionsResource,
553
954
  tenants: tenantsResource,
955
+ users: usersResource,
956
+ subscriptions: subscriptionsResource,
554
957
  audit: auditResource
555
958
  };
556
959
 
@@ -3887,7 +4290,16 @@ export {
3887
4290
  defineListOp,
3888
4291
  plansResource,
3889
4292
  planVersionsResource,
4293
+ bundlesResource,
4294
+ bundleVersionsResource,
4295
+ catalogResource,
4296
+ discoveryResource,
4297
+ marketingResource,
4298
+ promoCodesResource,
4299
+ promotionsResource,
3890
4300
  tenantsResource,
4301
+ usersResource,
4302
+ subscriptionsResource,
3891
4303
  auditResource,
3892
4304
  platformResources,
3893
4305
  BootLoadError,
@@ -4,7 +4,7 @@ import {
4
4
  bindResource,
5
5
  createSaCatalog,
6
6
  defaultKvStore
7
- } from "./chunk-BPF2BMCQ.js";
7
+ } from "./chunk-4IZWT4UY.js";
8
8
 
9
9
  // src/vue/super-admin-context.ts
10
10
  var SUPER_ADMIN_BRAND_KEY = /* @__PURE__ */ Symbol.for(
@@ -7,7 +7,7 @@ import {
7
7
  markTransportFailure,
8
8
  navMessages,
9
9
  trimTrailingSlashes
10
- } from "./chunk-BPF2BMCQ.js";
10
+ } from "./chunk-4IZWT4UY.js";
11
11
 
12
12
  // src/client/version.ts
13
13
  var ADMIN_UI_VERSION = "1.2.0";
@@ -116,7 +116,7 @@ var DEFAULT_STANDARD_PAGE_ROUTES = {
116
116
  platformEmailHistory: "/admin/platform-email-history"
117
117
  };
118
118
  function isSupportedStandardPageKey(key) {
119
- return Object.hasOwn(DEFAULT_STANDARD_PAGE_ROUTES, key);
119
+ return Object.prototype.hasOwnProperty.call(DEFAULT_STANDARD_PAGE_ROUTES, key);
120
120
  }
121
121
  var DEFAULT_ICONS = {
122
122
  dashboard: "dashboard",