@isaiahiroko/tilmart-ngx-apps-admin 0.2.0 → 0.2.2
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/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-articles.module-OeLRnPwN.mjs → isaiahiroko-tilmart-ngx-apps-admin-articles.module-Cpx9BZcg.mjs} +5 -5
- package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-articles.module-OeLRnPwN.mjs.map → isaiahiroko-tilmart-ngx-apps-admin-articles.module-Cpx9BZcg.mjs.map} +1 -1
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-attribute-templates.module-CwQnW1cH.mjs +239 -0
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-attribute-templates.module-CwQnW1cH.mjs.map +1 -0
- package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-audit.module-C9gvw8X2.mjs → isaiahiroko-tilmart-ngx-apps-admin-audit.module-D5YaCThD.mjs} +6 -6
- package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-audit.module-C9gvw8X2.mjs.map → isaiahiroko-tilmart-ngx-apps-admin-audit.module-D5YaCThD.mjs.map} +1 -1
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-banners.module-KSapnx6Y.mjs +76 -0
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-banners.module-KSapnx6Y.mjs.map +1 -0
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-brands.module-7I0ni-tQ.mjs +70 -0
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-brands.module-7I0ni-tQ.mjs.map +1 -0
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-catalog.module-DoxDNaNn.mjs +538 -0
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-catalog.module-DoxDNaNn.mjs.map +1 -0
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-categories.module-OTUfenf0.mjs +94 -0
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-categories.module-OTUfenf0.mjs.map +1 -0
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-claims.module-Dk9mz7Wj.mjs +137 -0
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-claims.module-Dk9mz7Wj.mjs.map +1 -0
- package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-compatibility-rules.module-xN-JUWwp.mjs → isaiahiroko-tilmart-ngx-apps-admin-compatibility-rules.module-JiFnNJj_.mjs} +6 -6
- package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-compatibility-rules.module-xN-JUWwp.mjs.map → isaiahiroko-tilmart-ngx-apps-admin-compatibility-rules.module-JiFnNJj_.mjs.map} +1 -1
- package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-content.module-DLdKAUqc.mjs → isaiahiroko-tilmart-ngx-apps-admin-content.module-Bx6Zj0WX.mjs} +5 -5
- package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-content.module-DLdKAUqc.mjs.map → isaiahiroko-tilmart-ngx-apps-admin-content.module-Bx6Zj0WX.mjs.map} +1 -1
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-coupons.module-CoRh6Hxq.mjs +85 -0
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-coupons.module-CoRh6Hxq.mjs.map +1 -0
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-customers.module-CslGiOdq.mjs +123 -0
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-customers.module-CslGiOdq.mjs.map +1 -0
- package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-data-quality.module-Bfgh3zE5.mjs → isaiahiroko-tilmart-ngx-apps-admin-data-quality.module-CvFmMoJe.mjs} +6 -6
- package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-data-quality.module-Bfgh3zE5.mjs.map → isaiahiroko-tilmart-ngx-apps-admin-data-quality.module-CvFmMoJe.mjs.map} +1 -1
- package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-data-requests.module-C6tPNlup.mjs → isaiahiroko-tilmart-ngx-apps-admin-data-requests.module-CuZwNVPO.mjs} +6 -6
- package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-data-requests.module-C6tPNlup.mjs.map → isaiahiroko-tilmart-ngx-apps-admin-data-requests.module-CuZwNVPO.mjs.map} +1 -1
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-flash-sales.module-BPRnC17l.mjs +83 -0
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-flash-sales.module-BPRnC17l.mjs.map +1 -0
- package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-homepage.module-BZ3UmxE3.mjs → isaiahiroko-tilmart-ngx-apps-admin-homepage.module-vtAoIwSu.mjs} +5 -5
- package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-homepage.module-BZ3UmxE3.mjs.map → isaiahiroko-tilmart-ngx-apps-admin-homepage.module-vtAoIwSu.mjs.map} +1 -1
- package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-inbox.module-BZDTa1L-.mjs → isaiahiroko-tilmart-ngx-apps-admin-inbox.module-CRkfjWQT.mjs} +6 -6
- package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-inbox.module-BZDTa1L-.mjs.map → isaiahiroko-tilmart-ngx-apps-admin-inbox.module-CRkfjWQT.mjs.map} +1 -1
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-list-page.base-Cl7JZ-Tp.mjs +66 -0
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-list-page.base-Cl7JZ-Tp.mjs.map +1 -0
- package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-macros.module-BO4bdER2.mjs → isaiahiroko-tilmart-ngx-apps-admin-macros.module-CElxL9W4.mjs} +5 -5
- package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-macros.module-BO4bdER2.mjs.map → isaiahiroko-tilmart-ngx-apps-admin-macros.module-CElxL9W4.mjs.map} +1 -1
- package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-moderation.module-CZOCrias.mjs → isaiahiroko-tilmart-ngx-apps-admin-moderation.module-PGmFX7nk.mjs} +6 -6
- package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-moderation.module-CZOCrias.mjs.map → isaiahiroko-tilmart-ngx-apps-admin-moderation.module-PGmFX7nk.mjs.map} +1 -1
- package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-navigation.module-DoKzzRgn.mjs → isaiahiroko-tilmart-ngx-apps-admin-navigation.module-De1Cvqqa.mjs} +6 -6
- package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-navigation.module-DoKzzRgn.mjs.map → isaiahiroko-tilmart-ngx-apps-admin-navigation.module-De1Cvqqa.mjs.map} +1 -1
- package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-order-board.module-DIOjhptn.mjs → isaiahiroko-tilmart-ngx-apps-admin-order-board.module-CmLNiEEP.mjs} +4 -4
- package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-order-board.module-DIOjhptn.mjs.map → isaiahiroko-tilmart-ngx-apps-admin-order-board.module-CmLNiEEP.mjs.map} +1 -1
- package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-orders.module-Ddw49QW0.mjs → isaiahiroko-tilmart-ngx-apps-admin-orders.module-Cfeb0QjH.mjs} +15 -33
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-orders.module-Cfeb0QjH.mjs.map +1 -0
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-payments.module-DstLDYM7.mjs +114 -0
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-payments.module-DstLDYM7.mjs.map +1 -0
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-promotions.module-DG03OY1q.mjs +94 -0
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-promotions.module-DG03OY1q.mjs.map +1 -0
- package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-queues.module-K-zYYVFy.mjs → isaiahiroko-tilmart-ngx-apps-admin-queues.module-D7iLwX0A.mjs} +6 -6
- package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-queues.module-K-zYYVFy.mjs.map → isaiahiroko-tilmart-ngx-apps-admin-queues.module-D7iLwX0A.mjs.map} +1 -1
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-redirects.module-oQw5Xo5T.mjs +78 -0
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-redirects.module-oQw5Xo5T.mjs.map +1 -0
- package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-referrals.module-QLNhuh7q.mjs → isaiahiroko-tilmart-ngx-apps-admin-referrals.module-BIAY4L_u.mjs} +9 -34
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-referrals.module-BIAY4L_u.mjs.map +1 -0
- package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-reports.module-DBZov0We.mjs → isaiahiroko-tilmart-ngx-apps-admin-reports.module-DORiIVLQ.mjs} +5 -5
- package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-reports.module-DBZov0We.mjs.map → isaiahiroko-tilmart-ngx-apps-admin-reports.module-DORiIVLQ.mjs.map} +1 -1
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-returns.module-C9Iu9CX6.mjs +163 -0
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-returns.module-C9Iu9CX6.mjs.map +1 -0
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-routes.module-CGBBHwId.mjs +81 -0
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-routes.module-CGBBHwId.mjs.map +1 -0
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-settings.module-Cz6xPNwB.mjs +318 -0
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-settings.module-Cz6xPNwB.mjs.map +1 -0
- package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-shipments.module-XuaOKr8v.mjs → isaiahiroko-tilmart-ngx-apps-admin-shipments.module-Cs5CLamm.mjs} +13 -32
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-shipments.module-Cs5CLamm.mjs.map +1 -0
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-simple-crud.base-C8SPw14H.mjs +140 -0
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-simple-crud.base-C8SPw14H.mjs.map +1 -0
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-stock.module-Bt9W1oGR.mjs +102 -0
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-stock.module-Bt9W1oGR.mjs.map +1 -0
- package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-trace.module-Pwr-82Rc.mjs → isaiahiroko-tilmart-ngx-apps-admin-trace.module-9sRlGZCC.mjs} +6 -6
- package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-trace.module-Pwr-82Rc.mjs.map → isaiahiroko-tilmart-ngx-apps-admin-trace.module-9sRlGZCC.mjs.map} +1 -1
- package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-users.module-Bx_7fI2g.mjs → isaiahiroko-tilmart-ngx-apps-admin-users.module-C31RJJJ_.mjs} +9 -34
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-users.module-C31RJJJ_.mjs.map +1 -0
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin.mjs +96 -72
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin.mjs.map +1 -1
- package/package.json +1 -1
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-baskets.module-_lHPl1dT.mjs +0 -294
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-baskets.module-_lHPl1dT.mjs.map +0 -1
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-catalog.module-Bvx2tVOi.mjs +0 -224
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-catalog.module-Bvx2tVOi.mjs.map +0 -1
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-claims.module-Ccsd8Yx3.mjs +0 -106
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-claims.module-Ccsd8Yx3.mjs.map +0 -1
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-coupons.module-CQNCSlsd.mjs +0 -106
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-coupons.module-CQNCSlsd.mjs.map +0 -1
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-customers.module-12XdV9zo.mjs +0 -106
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-customers.module-12XdV9zo.mjs.map +0 -1
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-orders.module-Ddw49QW0.mjs.map +0 -1
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-payments.module-BhqAtTZs.mjs +0 -106
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-payments.module-BhqAtTZs.mjs.map +0 -1
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-procurement.module-qXW-g0pf.mjs +0 -274
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-procurement.module-qXW-g0pf.mjs.map +0 -1
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-promotions.module-DG9uTDqX.mjs +0 -106
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-promotions.module-DG9uTDqX.mjs.map +0 -1
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-redirects.module-Hm2dO_bC.mjs +0 -106
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-redirects.module-Hm2dO_bC.mjs.map +0 -1
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-referrals.module-QLNhuh7q.mjs.map +0 -1
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-returns.module-Bd2gdo6_.mjs +0 -106
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-returns.module-Bd2gdo6_.mjs.map +0 -1
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-routes.module-BkVY06ci.mjs +0 -105
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-routes.module-BkVY06ci.mjs.map +0 -1
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-settings.module-DKeZTldO.mjs +0 -222
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-settings.module-DKeZTldO.mjs.map +0 -1
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-shipments.module-XuaOKr8v.mjs.map +0 -1
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-stock.module-DYiAkF7E.mjs +0 -107
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-stock.module-DYiAkF7E.mjs.map +0 -1
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-users.module-Bx_7fI2g.mjs.map +0 -1
|
@@ -1,294 +0,0 @@
|
|
|
1
|
-
import * as i0 from '@angular/core';
|
|
2
|
-
import { inject, TemplateRef, ViewChild, Component, NgModule } from '@angular/core';
|
|
3
|
-
import * as i1$2 from '@angular/router';
|
|
4
|
-
import { RouterModule } from '@angular/router';
|
|
5
|
-
import { BaseDirective, using, notify, titleCase, SharedModule } from '@thescaffold/ngx-core';
|
|
6
|
-
import * as i1$1 from '@thescaffold/ngx-ui';
|
|
7
|
-
import { FlyoutService, ElementsModule, WebgetsModule } from '@thescaffold/ngx-ui';
|
|
8
|
-
import { BehaviorSubject, takeUntil, map } from 'rxjs';
|
|
9
|
-
import * as i1 from '@angular/forms';
|
|
10
|
-
import * as i2 from '@angular/common';
|
|
11
|
-
|
|
12
|
-
const CADENCE_OPTIONS = ['monthly', 'weekly'];
|
|
13
|
-
const STATUS_OPTIONS = ['active', 'paused', 'cancelled'];
|
|
14
|
-
// Thin admin CRUD over the same apps/grocery/subscription endpoints the
|
|
15
|
-
// buyer app's own basket-builder uses (docs/PLAN_2.md Phase 10) - not a
|
|
16
|
-
// re-implementation of that guided flow. Editable surface here is
|
|
17
|
-
// deliberately just the operational fields a support agent would touch
|
|
18
|
-
// (name/cadence/fee/status/route); members/address/line items stay the
|
|
19
|
-
// buyer's own self-service surface. No "create" here for the same reason -
|
|
20
|
-
// baskets are created via the buyer app's template/tier/address wizard,
|
|
21
|
-
// which this page doesn't attempt to replicate.
|
|
22
|
-
class BasketsComponent extends BaseDirective {
|
|
23
|
-
constructor() {
|
|
24
|
-
super(...arguments);
|
|
25
|
-
this.flyoutService = inject(FlyoutService);
|
|
26
|
-
this.name = 'basket';
|
|
27
|
-
this.pluralName = 'baskets';
|
|
28
|
-
this.dataSource = this.apiService.on('apps/grocery/subscription');
|
|
29
|
-
this.routeSource = this.apiService.on('apps/grocery/route');
|
|
30
|
-
this.columns = [
|
|
31
|
-
{ key: 'name', value: 'Name' },
|
|
32
|
-
{ key: 'household.name', value: 'Household' },
|
|
33
|
-
{ key: 'household.phone', value: 'Phone' },
|
|
34
|
-
{ key: 'cadence', value: 'Cadence', type: 'class', class: 'badge text-bg-light border' },
|
|
35
|
-
{ key: 'status', value: 'Status', type: 'class', class: 'badge text-bg-light border' },
|
|
36
|
-
{ key: 'deliveryFee', value: 'Delivery fee' },
|
|
37
|
-
];
|
|
38
|
-
this.cadenceOptions = CADENCE_OPTIONS;
|
|
39
|
-
this.statusOptions = STATUS_OPTIONS;
|
|
40
|
-
this.routes = [];
|
|
41
|
-
this.page = 1;
|
|
42
|
-
this.perPage = 25;
|
|
43
|
-
this.list$ = new BehaviorSubject(null);
|
|
44
|
-
this.editing = null;
|
|
45
|
-
this.saving = false;
|
|
46
|
-
this.init = () => {
|
|
47
|
-
this.list();
|
|
48
|
-
this.routeSource
|
|
49
|
-
.getMany({ page: 1, perPage: 200 })
|
|
50
|
-
.pipe(takeUntil(this.$))
|
|
51
|
-
.subscribe(using(({ data }) => {
|
|
52
|
-
this.routes = data ?? [];
|
|
53
|
-
}));
|
|
54
|
-
};
|
|
55
|
-
}
|
|
56
|
-
list(page = this.page, perPage = this.perPage) {
|
|
57
|
-
this.dataSource
|
|
58
|
-
.getMany({ page, perPage, relations: 'household' })
|
|
59
|
-
.pipe(takeUntil(this.$), map((response) => this.toCollection(response)))
|
|
60
|
-
.subscribe(using((data) => this.list$.next(data)));
|
|
61
|
-
}
|
|
62
|
-
edit(item) {
|
|
63
|
-
this.editing = {
|
|
64
|
-
id: item.id,
|
|
65
|
-
name: item.name,
|
|
66
|
-
cadence: item.cadence,
|
|
67
|
-
// decimal columns come back as strings (node-postgres avoids float
|
|
68
|
-
// precision loss) - the number input's ngModel only coerces on an
|
|
69
|
-
// actual (input) event, so an untouched field would otherwise PATCH
|
|
70
|
-
// back out as a string and fail the DTO's @IsNumber() check.
|
|
71
|
-
deliveryFee: Number(item.deliveryFee ?? 0),
|
|
72
|
-
status: item.status,
|
|
73
|
-
routeId: item.routeId ?? null,
|
|
74
|
-
};
|
|
75
|
-
this.flyoutService.open(this.editFlyout);
|
|
76
|
-
}
|
|
77
|
-
save() {
|
|
78
|
-
if (!this.editing || this.saving) {
|
|
79
|
-
return;
|
|
80
|
-
}
|
|
81
|
-
this.saving = true;
|
|
82
|
-
const { id, routeId, ...rest } = this.editing;
|
|
83
|
-
// @IsOptional() only skips validation on `undefined`, not `null` - an
|
|
84
|
-
// explicit `routeId: null` fails the DTO's @IsUUID() check, so omit it
|
|
85
|
-
// entirely when no route is assigned instead of sending null.
|
|
86
|
-
const payload = { ...rest, deliveryFee: Number(rest.deliveryFee) };
|
|
87
|
-
if (routeId) {
|
|
88
|
-
payload.routeId = routeId;
|
|
89
|
-
}
|
|
90
|
-
this.dataSource
|
|
91
|
-
.update(id, payload)
|
|
92
|
-
.pipe(takeUntil(this.$))
|
|
93
|
-
.subscribe(using(() => {
|
|
94
|
-
notify('Basket updated', 'apps.admin.baskets.save', 'success');
|
|
95
|
-
this.saving = false;
|
|
96
|
-
this.flyoutService.dismiss();
|
|
97
|
-
this.list();
|
|
98
|
-
},
|
|
99
|
-
// using()'s default error handler is what shows a toast on
|
|
100
|
-
// failure - overriding it (to also reset `saving`) means doing
|
|
101
|
-
// that notify() call ourselves too, or a validation error fails
|
|
102
|
-
// silently in the UI with nothing but a console 400.
|
|
103
|
-
(err) => {
|
|
104
|
-
this.saving = false;
|
|
105
|
-
err?.message && notify(err.message);
|
|
106
|
-
}));
|
|
107
|
-
}
|
|
108
|
-
togglePause(item) {
|
|
109
|
-
const action = item.status === 'paused' ? 'resume' : 'pause';
|
|
110
|
-
this.apiService
|
|
111
|
-
.post(`apps/grocery/subscription/${item.id}/${action}`, {})
|
|
112
|
-
.pipe(takeUntil(this.$))
|
|
113
|
-
.subscribe(using(() => {
|
|
114
|
-
notify(action === 'pause' ? 'Basket paused' : 'Basket resumed', 'apps.admin.baskets.toggle-pause', 'success');
|
|
115
|
-
this.list();
|
|
116
|
-
}));
|
|
117
|
-
}
|
|
118
|
-
toCollection(response) {
|
|
119
|
-
return {
|
|
120
|
-
title: titleCase(this.pluralName),
|
|
121
|
-
columns: this.columns,
|
|
122
|
-
rows: response?.data,
|
|
123
|
-
hasHeaders: true,
|
|
124
|
-
hasTextFilter: true,
|
|
125
|
-
page: response?.meta?.current_page,
|
|
126
|
-
perPage: response?.meta?.per_page,
|
|
127
|
-
total: response?.meta?.total,
|
|
128
|
-
onPage: (page) => this.list(page),
|
|
129
|
-
links: {
|
|
130
|
-
secondary: [
|
|
131
|
-
{
|
|
132
|
-
title: 'Edit',
|
|
133
|
-
fn: (link, item) => this.edit(item),
|
|
134
|
-
},
|
|
135
|
-
{
|
|
136
|
-
// Static label - the collection template interpolates `title`
|
|
137
|
-
// directly rather than invoking it, so it can't reflect each
|
|
138
|
-
// row's current status; togglePause() itself picks the right
|
|
139
|
-
// direction from the clicked item at call time.
|
|
140
|
-
title: 'Pause / Resume',
|
|
141
|
-
fn: (link, item) => this.togglePause(item),
|
|
142
|
-
},
|
|
143
|
-
],
|
|
144
|
-
},
|
|
145
|
-
};
|
|
146
|
-
}
|
|
147
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: BasketsComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
|
|
148
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.16", type: BasketsComponent, isStandalone: false, selector: "x-baskets", viewQueries: [{ propertyName: "editFlyout", first: true, predicate: ["editFlyout"], descendants: true, read: TemplateRef, static: true }], usesInheritance: true, ngImport: i0, template: `
|
|
149
|
-
<div class="x-page-head">
|
|
150
|
-
<div class="x-page-title">Baskets</div>
|
|
151
|
-
<div class="x-page-subtitle">Every household's baskets, across all workspaces.</div>
|
|
152
|
-
</div>
|
|
153
|
-
|
|
154
|
-
@if (list$ | async; as list) {
|
|
155
|
-
<x-webget-collection [data]="list"></x-webget-collection>
|
|
156
|
-
} @else {
|
|
157
|
-
<x-element-loader></x-element-loader>
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
<ng-template #editFlyout>
|
|
161
|
-
<x-webget-flyout [data]="{ title: 'Edit basket' }">
|
|
162
|
-
@if (editing) {
|
|
163
|
-
<div class="p-4 d-flex flex-column gap-3">
|
|
164
|
-
<div>
|
|
165
|
-
<label class="form-label">Name</label>
|
|
166
|
-
<input type="text" class="form-control" [(ngModel)]="editing.name" />
|
|
167
|
-
</div>
|
|
168
|
-
<div>
|
|
169
|
-
<label class="form-label">Cadence</label>
|
|
170
|
-
<select class="form-select" [(ngModel)]="editing.cadence">
|
|
171
|
-
@for (c of cadenceOptions; track c) {
|
|
172
|
-
<option [value]="c">{{ c }}</option>
|
|
173
|
-
}
|
|
174
|
-
</select>
|
|
175
|
-
</div>
|
|
176
|
-
<div>
|
|
177
|
-
<label class="form-label">Delivery fee (₦)</label>
|
|
178
|
-
<input type="number" class="form-control" [(ngModel)]="editing.deliveryFee" />
|
|
179
|
-
</div>
|
|
180
|
-
<div>
|
|
181
|
-
<label class="form-label">Status</label>
|
|
182
|
-
<select class="form-select" [(ngModel)]="editing.status">
|
|
183
|
-
@for (s of statusOptions; track s) {
|
|
184
|
-
<option [value]="s">{{ s }}</option>
|
|
185
|
-
}
|
|
186
|
-
</select>
|
|
187
|
-
</div>
|
|
188
|
-
<div>
|
|
189
|
-
<label class="form-label">Route</label>
|
|
190
|
-
<select class="form-select" [(ngModel)]="editing.routeId">
|
|
191
|
-
<option [ngValue]="null">— none —</option>
|
|
192
|
-
@for (r of routes; track r.id) {
|
|
193
|
-
<option [value]="r.id">{{ r.name }}</option>
|
|
194
|
-
}
|
|
195
|
-
</select>
|
|
196
|
-
</div>
|
|
197
|
-
<button type="button" class="btn btn-success" [disabled]="saving" (click)="save()">
|
|
198
|
-
{{ saving ? 'Saving…' : 'Save' }}
|
|
199
|
-
</button>
|
|
200
|
-
</div>
|
|
201
|
-
}
|
|
202
|
-
</x-webget-flyout>
|
|
203
|
-
</ng-template>
|
|
204
|
-
`, isInline: true, styles: [".x-page-head{margin-bottom:20px}.x-page-title{font-size:24px;font-weight:800;color:var(--color-text)}.x-page-subtitle{font-size:13.5px;color:var(--color-text-muted);margin-top:2px}\n"], dependencies: [{ kind: "directive", type: i1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: i1$1.LoaderComponent, selector: "x-element-loader", inputs: ["data"] }, { kind: "component", type: i1$1.CollectionComponent, selector: "x-webget-collection", inputs: ["data"], outputs: ["search", "filter", "refresh", "select", "page", "show"] }, { kind: "component", type: i1$1.FlyoutComponent, selector: "x-webget-flyout", inputs: ["data"] }, { kind: "pipe", type: i2.AsyncPipe, name: "async" }] }); }
|
|
205
|
-
}
|
|
206
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: BasketsComponent, decorators: [{
|
|
207
|
-
type: Component,
|
|
208
|
-
args: [{ standalone: false, selector: 'x-baskets', template: `
|
|
209
|
-
<div class="x-page-head">
|
|
210
|
-
<div class="x-page-title">Baskets</div>
|
|
211
|
-
<div class="x-page-subtitle">Every household's baskets, across all workspaces.</div>
|
|
212
|
-
</div>
|
|
213
|
-
|
|
214
|
-
@if (list$ | async; as list) {
|
|
215
|
-
<x-webget-collection [data]="list"></x-webget-collection>
|
|
216
|
-
} @else {
|
|
217
|
-
<x-element-loader></x-element-loader>
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
<ng-template #editFlyout>
|
|
221
|
-
<x-webget-flyout [data]="{ title: 'Edit basket' }">
|
|
222
|
-
@if (editing) {
|
|
223
|
-
<div class="p-4 d-flex flex-column gap-3">
|
|
224
|
-
<div>
|
|
225
|
-
<label class="form-label">Name</label>
|
|
226
|
-
<input type="text" class="form-control" [(ngModel)]="editing.name" />
|
|
227
|
-
</div>
|
|
228
|
-
<div>
|
|
229
|
-
<label class="form-label">Cadence</label>
|
|
230
|
-
<select class="form-select" [(ngModel)]="editing.cadence">
|
|
231
|
-
@for (c of cadenceOptions; track c) {
|
|
232
|
-
<option [value]="c">{{ c }}</option>
|
|
233
|
-
}
|
|
234
|
-
</select>
|
|
235
|
-
</div>
|
|
236
|
-
<div>
|
|
237
|
-
<label class="form-label">Delivery fee (₦)</label>
|
|
238
|
-
<input type="number" class="form-control" [(ngModel)]="editing.deliveryFee" />
|
|
239
|
-
</div>
|
|
240
|
-
<div>
|
|
241
|
-
<label class="form-label">Status</label>
|
|
242
|
-
<select class="form-select" [(ngModel)]="editing.status">
|
|
243
|
-
@for (s of statusOptions; track s) {
|
|
244
|
-
<option [value]="s">{{ s }}</option>
|
|
245
|
-
}
|
|
246
|
-
</select>
|
|
247
|
-
</div>
|
|
248
|
-
<div>
|
|
249
|
-
<label class="form-label">Route</label>
|
|
250
|
-
<select class="form-select" [(ngModel)]="editing.routeId">
|
|
251
|
-
<option [ngValue]="null">— none —</option>
|
|
252
|
-
@for (r of routes; track r.id) {
|
|
253
|
-
<option [value]="r.id">{{ r.name }}</option>
|
|
254
|
-
}
|
|
255
|
-
</select>
|
|
256
|
-
</div>
|
|
257
|
-
<button type="button" class="btn btn-success" [disabled]="saving" (click)="save()">
|
|
258
|
-
{{ saving ? 'Saving…' : 'Save' }}
|
|
259
|
-
</button>
|
|
260
|
-
</div>
|
|
261
|
-
}
|
|
262
|
-
</x-webget-flyout>
|
|
263
|
-
</ng-template>
|
|
264
|
-
`, styles: [".x-page-head{margin-bottom:20px}.x-page-title{font-size:24px;font-weight:800;color:var(--color-text)}.x-page-subtitle{font-size:13.5px;color:var(--color-text-muted);margin-top:2px}\n"] }]
|
|
265
|
-
}], propDecorators: { editFlyout: [{
|
|
266
|
-
type: ViewChild,
|
|
267
|
-
args: ['editFlyout', { read: TemplateRef, static: true }]
|
|
268
|
-
}] } });
|
|
269
|
-
|
|
270
|
-
class BasketsModule {
|
|
271
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: BasketsModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
|
|
272
|
-
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.2.16", ngImport: i0, type: BasketsModule, declarations: [BasketsComponent], imports: [SharedModule,
|
|
273
|
-
ElementsModule,
|
|
274
|
-
WebgetsModule, i1$2.RouterModule] }); }
|
|
275
|
-
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: BasketsModule, imports: [SharedModule,
|
|
276
|
-
ElementsModule,
|
|
277
|
-
WebgetsModule,
|
|
278
|
-
RouterModule.forChild([{ path: '', component: BasketsComponent }])] }); }
|
|
279
|
-
}
|
|
280
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: BasketsModule, decorators: [{
|
|
281
|
-
type: NgModule,
|
|
282
|
-
args: [{
|
|
283
|
-
declarations: [BasketsComponent],
|
|
284
|
-
imports: [
|
|
285
|
-
SharedModule,
|
|
286
|
-
ElementsModule,
|
|
287
|
-
WebgetsModule,
|
|
288
|
-
RouterModule.forChild([{ path: '', component: BasketsComponent }]),
|
|
289
|
-
],
|
|
290
|
-
}]
|
|
291
|
-
}] });
|
|
292
|
-
|
|
293
|
-
export { BasketsModule };
|
|
294
|
-
//# sourceMappingURL=isaiahiroko-tilmart-ngx-apps-admin-baskets.module-_lHPl1dT.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"isaiahiroko-tilmart-ngx-apps-admin-baskets.module-_lHPl1dT.mjs","sources":["../../../projects/admin/src/lib/pages/baskets/baskets.component.ts","../../../projects/admin/src/lib/pages/baskets/baskets.module.ts"],"sourcesContent":["import { Component, TemplateRef, ViewChild, inject } from '@angular/core';\nimport { BaseDirective, notify, titleCase, using } from '@thescaffold/ngx-core';\nimport { FlyoutService } from '@thescaffold/ngx-ui';\nimport { BehaviorSubject, takeUntil, map, forkJoin } from 'rxjs';\n\nconst CADENCE_OPTIONS = ['monthly', 'weekly'];\nconst STATUS_OPTIONS = ['active', 'paused', 'cancelled'];\n\n// Thin admin CRUD over the same apps/grocery/subscription endpoints the\n// buyer app's own basket-builder uses (docs/PLAN_2.md Phase 10) - not a\n// re-implementation of that guided flow. Editable surface here is\n// deliberately just the operational fields a support agent would touch\n// (name/cadence/fee/status/route); members/address/line items stay the\n// buyer's own self-service surface. No \"create\" here for the same reason -\n// baskets are created via the buyer app's template/tier/address wizard,\n// which this page doesn't attempt to replicate.\n@Component({\n standalone: false,\n selector: 'x-baskets',\n template: `\n <div class=\"x-page-head\">\n <div class=\"x-page-title\">Baskets</div>\n <div class=\"x-page-subtitle\">Every household's baskets, across all workspaces.</div>\n </div>\n\n @if (list$ | async; as list) {\n <x-webget-collection [data]=\"list\"></x-webget-collection>\n } @else {\n <x-element-loader></x-element-loader>\n }\n\n <ng-template #editFlyout>\n <x-webget-flyout [data]=\"{ title: 'Edit basket' }\">\n @if (editing) {\n <div class=\"p-4 d-flex flex-column gap-3\">\n <div>\n <label class=\"form-label\">Name</label>\n <input type=\"text\" class=\"form-control\" [(ngModel)]=\"editing.name\" />\n </div>\n <div>\n <label class=\"form-label\">Cadence</label>\n <select class=\"form-select\" [(ngModel)]=\"editing.cadence\">\n @for (c of cadenceOptions; track c) {\n <option [value]=\"c\">{{ c }}</option>\n }\n </select>\n </div>\n <div>\n <label class=\"form-label\">Delivery fee (₦)</label>\n <input type=\"number\" class=\"form-control\" [(ngModel)]=\"editing.deliveryFee\" />\n </div>\n <div>\n <label class=\"form-label\">Status</label>\n <select class=\"form-select\" [(ngModel)]=\"editing.status\">\n @for (s of statusOptions; track s) {\n <option [value]=\"s\">{{ s }}</option>\n }\n </select>\n </div>\n <div>\n <label class=\"form-label\">Route</label>\n <select class=\"form-select\" [(ngModel)]=\"editing.routeId\">\n <option [ngValue]=\"null\">— none —</option>\n @for (r of routes; track r.id) {\n <option [value]=\"r.id\">{{ r.name }}</option>\n }\n </select>\n </div>\n <button type=\"button\" class=\"btn btn-success\" [disabled]=\"saving\" (click)=\"save()\">\n {{ saving ? 'Saving…' : 'Save' }}\n </button>\n </div>\n }\n </x-webget-flyout>\n </ng-template>\n `,\n styles: [\n `\n .x-page-head {\n margin-bottom: 20px;\n }\n .x-page-title {\n font-size: 24px;\n font-weight: 800;\n color: var(--color-text);\n }\n .x-page-subtitle {\n font-size: 13.5px;\n color: var(--color-text-muted);\n margin-top: 2px;\n }\n `,\n ],\n})\nexport class BasketsComponent extends BaseDirective {\n private readonly flyoutService = inject(FlyoutService);\n\n name = 'basket';\n pluralName = 'baskets';\n dataSource = this.apiService.on('apps/grocery/subscription');\n private readonly routeSource = this.apiService.on('apps/grocery/route');\n\n columns = [\n { key: 'name', value: 'Name' },\n { key: 'household.name', value: 'Household' },\n { key: 'household.phone', value: 'Phone' },\n { key: 'cadence', value: 'Cadence', type: 'class', class: 'badge text-bg-light border' },\n { key: 'status', value: 'Status', type: 'class', class: 'badge text-bg-light border' },\n { key: 'deliveryFee', value: 'Delivery fee' },\n ];\n\n cadenceOptions = CADENCE_OPTIONS;\n statusOptions = STATUS_OPTIONS;\n routes: { id: string; name: string }[] = [];\n\n page = 1;\n perPage = 25;\n\n list$ = new BehaviorSubject<any>(null);\n editing: any = null;\n saving = false;\n\n @ViewChild('editFlyout', { read: TemplateRef, static: true })\n editFlyout!: TemplateRef<any>;\n\n override init = () => {\n this.list();\n this.routeSource\n .getMany({ page: 1, perPage: 200 })\n .pipe(takeUntil(this.$))\n .subscribe(\n using(({ data }: { data: any[] }) => {\n this.routes = data ?? [];\n }),\n );\n };\n\n list(page = this.page, perPage = this.perPage) {\n this.dataSource\n .getMany({ page, perPage, relations: 'household' } as any)\n .pipe(\n takeUntil(this.$),\n map((response) => this.toCollection(response)),\n )\n .subscribe(using((data) => this.list$.next(data)));\n }\n\n edit(item: any) {\n this.editing = {\n id: item.id,\n name: item.name,\n cadence: item.cadence,\n // decimal columns come back as strings (node-postgres avoids float\n // precision loss) - the number input's ngModel only coerces on an\n // actual (input) event, so an untouched field would otherwise PATCH\n // back out as a string and fail the DTO's @IsNumber() check.\n deliveryFee: Number(item.deliveryFee ?? 0),\n status: item.status,\n routeId: item.routeId ?? null,\n };\n this.flyoutService.open(this.editFlyout);\n }\n\n save() {\n if (!this.editing || this.saving) {\n return;\n }\n this.saving = true;\n const { id, routeId, ...rest } = this.editing;\n // @IsOptional() only skips validation on `undefined`, not `null` - an\n // explicit `routeId: null` fails the DTO's @IsUUID() check, so omit it\n // entirely when no route is assigned instead of sending null.\n const payload: any = { ...rest, deliveryFee: Number(rest.deliveryFee) };\n if (routeId) {\n payload.routeId = routeId;\n }\n this.dataSource\n .update(id, payload)\n .pipe(takeUntil(this.$))\n .subscribe(\n using(\n () => {\n notify('Basket updated', 'apps.admin.baskets.save', 'success');\n this.saving = false;\n this.flyoutService.dismiss();\n this.list();\n },\n // using()'s default error handler is what shows a toast on\n // failure - overriding it (to also reset `saving`) means doing\n // that notify() call ourselves too, or a validation error fails\n // silently in the UI with nothing but a console 400.\n (err: any) => {\n this.saving = false;\n err?.message && notify(err.message);\n },\n ),\n );\n }\n\n togglePause(item: any) {\n const action = item.status === 'paused' ? 'resume' : 'pause';\n this.apiService\n .post(`apps/grocery/subscription/${item.id}/${action}`, {})\n .pipe(takeUntil(this.$))\n .subscribe(\n using(() => {\n notify(\n action === 'pause' ? 'Basket paused' : 'Basket resumed',\n 'apps.admin.baskets.toggle-pause',\n 'success',\n );\n this.list();\n }),\n );\n }\n\n private toCollection(response: any) {\n return {\n title: titleCase(this.pluralName),\n columns: this.columns,\n rows: response?.data,\n hasHeaders: true,\n hasTextFilter: true,\n page: response?.meta?.current_page,\n perPage: response?.meta?.per_page,\n total: response?.meta?.total,\n onPage: (page: number) => this.list(page),\n links: {\n secondary: [\n {\n title: 'Edit',\n fn: (link: any, item: any) => this.edit(item),\n },\n {\n // Static label - the collection template interpolates `title`\n // directly rather than invoking it, so it can't reflect each\n // row's current status; togglePause() itself picks the right\n // direction from the clicked item at call time.\n title: 'Pause / Resume',\n fn: (link: any, item: any) => this.togglePause(item),\n },\n ],\n },\n };\n }\n}\n","import { NgModule } from '@angular/core';\nimport { RouterModule } from '@angular/router';\nimport { SharedModule } from '@thescaffold/ngx-core';\nimport { ElementsModule, WebgetsModule } from '@thescaffold/ngx-ui';\nimport { BasketsComponent } from './baskets.component';\n\n@NgModule({\n declarations: [BasketsComponent],\n imports: [\n SharedModule,\n ElementsModule,\n WebgetsModule,\n RouterModule.forChild([{ path: '', component: BasketsComponent }]),\n ],\n})\nexport class BasketsModule {}\n"],"names":["i2","i3","i1"],"mappings":"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"}
|
|
@@ -1,224 +0,0 @@
|
|
|
1
|
-
import * as i0 from '@angular/core';
|
|
2
|
-
import { Component, NgModule } from '@angular/core';
|
|
3
|
-
import * as i1$1 from '@angular/router';
|
|
4
|
-
import { RouterModule } from '@angular/router';
|
|
5
|
-
import { BaseDirective, using, SharedModule } from '@thescaffold/ngx-core';
|
|
6
|
-
import * as i1 from '@thescaffold/ngx-ui';
|
|
7
|
-
import { ElementsModule, WebgetsModule } from '@thescaffold/ngx-ui';
|
|
8
|
-
import { BehaviorSubject, takeUntil, map } from 'rxjs';
|
|
9
|
-
import * as i2 from '@angular/common';
|
|
10
|
-
|
|
11
|
-
// P1.15 — the catalogue editor's first slice. The Salama-era version of
|
|
12
|
-
// this page (basket templates + a bespoke cost-price re-quote table) was
|
|
13
|
-
// wholesale grocery-subscription domain and is gone; `ProductCostPrice`
|
|
14
|
-
// now has its own generic CRUD screen like every other resource. What
|
|
15
|
-
// replaces the bespoke half here is the thing the real catalogue actually
|
|
16
|
-
// needs per row: CAT-03's publish-validation check
|
|
17
|
-
// (`CatalogWorkflow.validatePublish`, P1.10) surfaced with field-level
|
|
18
|
-
// errors, not a single opaque pass/fail — same "per-row action, not just
|
|
19
|
-
// a read view" shape the old cost-price table had, new domain.
|
|
20
|
-
//
|
|
21
|
-
// A full dynamic per-attribute-type input grid (the more literal reading
|
|
22
|
-
// of "per-row attribute inputs") is deliberately not attempted here — it
|
|
23
|
-
// needs a form control per `AttributeDataType` (number/enum/boolean/
|
|
24
|
-
// range/text) driven by whichever template the selected product's
|
|
25
|
-
// category resolves to, which is real, isolated follow-up work once a
|
|
26
|
-
// live category/template dataset exists to build and check it against,
|
|
27
|
-
// not something to guess the shape of blind.
|
|
28
|
-
class CatalogComponent extends BaseDirective {
|
|
29
|
-
constructor() {
|
|
30
|
-
super(...arguments);
|
|
31
|
-
this.name = 'product';
|
|
32
|
-
this.pluralName = 'products';
|
|
33
|
-
this.productSource = this.apiService.on('apps/commerce/product');
|
|
34
|
-
this.products$ = new BehaviorSubject(null);
|
|
35
|
-
this.init = () => {
|
|
36
|
-
this.loadProducts();
|
|
37
|
-
};
|
|
38
|
-
}
|
|
39
|
-
loadProducts() {
|
|
40
|
-
this.productSource
|
|
41
|
-
.getMany({ page: 1, perPage: 100 })
|
|
42
|
-
.pipe(takeUntil(this.$), map((response) => (response?.data ?? []).map((p) => ({
|
|
43
|
-
id: p.id,
|
|
44
|
-
name: p.name,
|
|
45
|
-
model: p.model,
|
|
46
|
-
type: p.type,
|
|
47
|
-
status: p.status,
|
|
48
|
-
updatedAt: p.updatedAt,
|
|
49
|
-
checking: false,
|
|
50
|
-
checked: false,
|
|
51
|
-
}))))
|
|
52
|
-
.subscribe(using((rows) => this.products$.next(rows)));
|
|
53
|
-
}
|
|
54
|
-
checkPublish(product) {
|
|
55
|
-
product.checking = true;
|
|
56
|
-
this.productSource
|
|
57
|
-
.post(`apps/commerce/product/${product.id}/validate-publish`)
|
|
58
|
-
.pipe(takeUntil(this.$))
|
|
59
|
-
.subscribe(using((response) => {
|
|
60
|
-
product.checking = false;
|
|
61
|
-
product.checked = true;
|
|
62
|
-
product.canPublish = !!response?.data?.canPublish;
|
|
63
|
-
product.errors = response?.data?.errors ?? [];
|
|
64
|
-
}));
|
|
65
|
-
}
|
|
66
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: CatalogComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
|
|
67
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.16", type: CatalogComponent, isStandalone: false, selector: "x-catalog", usesInheritance: true, ngImport: i0, template: `
|
|
68
|
-
<div class="x-page-head">
|
|
69
|
-
<div class="x-page-title">Catalogue</div>
|
|
70
|
-
<div class="x-page-subtitle">
|
|
71
|
-
Products, variants and their publish readiness (CAT-03).
|
|
72
|
-
</div>
|
|
73
|
-
</div>
|
|
74
|
-
|
|
75
|
-
@if (products$ | async; as products) {
|
|
76
|
-
<div class="x-catalog-table">
|
|
77
|
-
<div class="x-catalog-row x-catalog-head">
|
|
78
|
-
<div class="x-catalog-col-name">Product</div>
|
|
79
|
-
<div class="x-catalog-col-type">Type</div>
|
|
80
|
-
<div class="x-catalog-col-status">Status</div>
|
|
81
|
-
<div class="x-catalog-col-check">Publish check</div>
|
|
82
|
-
<div class="x-catalog-col-action"></div>
|
|
83
|
-
</div>
|
|
84
|
-
@for (product of products; track product.id) {
|
|
85
|
-
<div class="x-catalog-row">
|
|
86
|
-
<div class="x-catalog-col-name">
|
|
87
|
-
{{ product.name }}
|
|
88
|
-
@if (product.model) {
|
|
89
|
-
<span class="x-catalog-model">{{ product.model }}</span>
|
|
90
|
-
}
|
|
91
|
-
</div>
|
|
92
|
-
<div class="x-catalog-col-type">{{ product.type }}</div>
|
|
93
|
-
<div class="x-catalog-col-status">
|
|
94
|
-
<span class="x-status-badge" [class]="'x-status-' + product.status">{{
|
|
95
|
-
product.status
|
|
96
|
-
}}</span>
|
|
97
|
-
</div>
|
|
98
|
-
<div class="x-catalog-col-check">
|
|
99
|
-
@if (product.checking) {
|
|
100
|
-
<span class="x-check-pending">Checking…</span>
|
|
101
|
-
} @else if (product.checked && product.canPublish) {
|
|
102
|
-
<span class="x-check-ok">✓ Ready to publish</span>
|
|
103
|
-
} @else if (product.checked && product.errors?.length) {
|
|
104
|
-
<ul class="x-check-errors">
|
|
105
|
-
@for (err of product.errors; track err.field) {
|
|
106
|
-
<li>{{ err.message }}</li>
|
|
107
|
-
}
|
|
108
|
-
</ul>
|
|
109
|
-
}
|
|
110
|
-
</div>
|
|
111
|
-
<div class="x-catalog-col-action">
|
|
112
|
-
<button
|
|
113
|
-
type="button"
|
|
114
|
-
class="x-check-btn"
|
|
115
|
-
[disabled]="product.checking"
|
|
116
|
-
(click)="checkPublish(product)"
|
|
117
|
-
>
|
|
118
|
-
Check
|
|
119
|
-
</button>
|
|
120
|
-
</div>
|
|
121
|
-
</div>
|
|
122
|
-
}
|
|
123
|
-
@if (!products.length) {
|
|
124
|
-
<div class="x-catalog-empty">No products yet.</div>
|
|
125
|
-
}
|
|
126
|
-
</div>
|
|
127
|
-
} @else {
|
|
128
|
-
<x-element-loader></x-element-loader>
|
|
129
|
-
}
|
|
130
|
-
`, isInline: true, styles: [".x-page-head{margin-bottom:20px}.x-page-title{font-size:24px;font-weight:800;color:var(--color-text)}.x-page-subtitle{font-size:13.5px;color:var(--color-text-muted);margin-top:2px}.x-catalog-table{background:var(--color-surface);border-radius:var(--radius-lg);overflow:hidden}.x-catalog-row{display:grid;grid-template-columns:1.6fr .8fr .8fr 1.6fr auto;gap:12px;align-items:center;padding:12px 16px;border-bottom:1px solid var(--color-border-muted)}.x-catalog-row:last-child{border-bottom:0}.x-catalog-head{font-size:11px;font-weight:800;text-transform:uppercase;letter-spacing:.4px;color:var(--color-text-muted)}.x-catalog-col-name{font-size:14px;font-weight:700;color:var(--color-text)}.x-catalog-model{display:block;font-family:var(--font-mono);font-size:11.5px;font-weight:500;color:var(--color-text-muted)}.x-catalog-col-type{font-size:13px;color:var(--color-text-muted);text-transform:capitalize}.x-status-badge{display:inline-block;font-size:10.5px;font-weight:800;text-transform:uppercase;letter-spacing:.3px;padding:2px 8px;border-radius:var(--radius-pill, 999px);background:var(--color-surface-muted, var(--color-bg));border:1px solid var(--color-border);color:var(--color-text-muted)}.x-status-published{color:color-mix(in srgb,var(--color-success) 70%,black);border-color:var(--color-success)}.x-check-pending{font-size:12.5px;color:var(--color-text-muted)}.x-check-ok{font-size:12.5px;font-weight:700;color:var(--color-success)}.x-check-errors{margin:0;padding-left:16px;font-size:12px;color:color-mix(in srgb,var(--color-danger) 80%,black)}.x-check-btn{background:var(--color-accent);color:var(--color-accent-fg);border:0;border-radius:var(--radius-sm);padding:8px 12px;font-size:12.5px;font-weight:700;cursor:pointer;white-space:nowrap}.x-check-btn:disabled{opacity:.5;cursor:not-allowed}.x-catalog-empty{padding:32px 16px;text-align:center;font-size:13px;color:var(--color-text-muted)}\n"], dependencies: [{ kind: "component", type: i1.LoaderComponent, selector: "x-element-loader", inputs: ["data"] }, { kind: "pipe", type: i2.AsyncPipe, name: "async" }] }); }
|
|
131
|
-
}
|
|
132
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: CatalogComponent, decorators: [{
|
|
133
|
-
type: Component,
|
|
134
|
-
args: [{ standalone: false, selector: 'x-catalog', template: `
|
|
135
|
-
<div class="x-page-head">
|
|
136
|
-
<div class="x-page-title">Catalogue</div>
|
|
137
|
-
<div class="x-page-subtitle">
|
|
138
|
-
Products, variants and their publish readiness (CAT-03).
|
|
139
|
-
</div>
|
|
140
|
-
</div>
|
|
141
|
-
|
|
142
|
-
@if (products$ | async; as products) {
|
|
143
|
-
<div class="x-catalog-table">
|
|
144
|
-
<div class="x-catalog-row x-catalog-head">
|
|
145
|
-
<div class="x-catalog-col-name">Product</div>
|
|
146
|
-
<div class="x-catalog-col-type">Type</div>
|
|
147
|
-
<div class="x-catalog-col-status">Status</div>
|
|
148
|
-
<div class="x-catalog-col-check">Publish check</div>
|
|
149
|
-
<div class="x-catalog-col-action"></div>
|
|
150
|
-
</div>
|
|
151
|
-
@for (product of products; track product.id) {
|
|
152
|
-
<div class="x-catalog-row">
|
|
153
|
-
<div class="x-catalog-col-name">
|
|
154
|
-
{{ product.name }}
|
|
155
|
-
@if (product.model) {
|
|
156
|
-
<span class="x-catalog-model">{{ product.model }}</span>
|
|
157
|
-
}
|
|
158
|
-
</div>
|
|
159
|
-
<div class="x-catalog-col-type">{{ product.type }}</div>
|
|
160
|
-
<div class="x-catalog-col-status">
|
|
161
|
-
<span class="x-status-badge" [class]="'x-status-' + product.status">{{
|
|
162
|
-
product.status
|
|
163
|
-
}}</span>
|
|
164
|
-
</div>
|
|
165
|
-
<div class="x-catalog-col-check">
|
|
166
|
-
@if (product.checking) {
|
|
167
|
-
<span class="x-check-pending">Checking…</span>
|
|
168
|
-
} @else if (product.checked && product.canPublish) {
|
|
169
|
-
<span class="x-check-ok">✓ Ready to publish</span>
|
|
170
|
-
} @else if (product.checked && product.errors?.length) {
|
|
171
|
-
<ul class="x-check-errors">
|
|
172
|
-
@for (err of product.errors; track err.field) {
|
|
173
|
-
<li>{{ err.message }}</li>
|
|
174
|
-
}
|
|
175
|
-
</ul>
|
|
176
|
-
}
|
|
177
|
-
</div>
|
|
178
|
-
<div class="x-catalog-col-action">
|
|
179
|
-
<button
|
|
180
|
-
type="button"
|
|
181
|
-
class="x-check-btn"
|
|
182
|
-
[disabled]="product.checking"
|
|
183
|
-
(click)="checkPublish(product)"
|
|
184
|
-
>
|
|
185
|
-
Check
|
|
186
|
-
</button>
|
|
187
|
-
</div>
|
|
188
|
-
</div>
|
|
189
|
-
}
|
|
190
|
-
@if (!products.length) {
|
|
191
|
-
<div class="x-catalog-empty">No products yet.</div>
|
|
192
|
-
}
|
|
193
|
-
</div>
|
|
194
|
-
} @else {
|
|
195
|
-
<x-element-loader></x-element-loader>
|
|
196
|
-
}
|
|
197
|
-
`, styles: [".x-page-head{margin-bottom:20px}.x-page-title{font-size:24px;font-weight:800;color:var(--color-text)}.x-page-subtitle{font-size:13.5px;color:var(--color-text-muted);margin-top:2px}.x-catalog-table{background:var(--color-surface);border-radius:var(--radius-lg);overflow:hidden}.x-catalog-row{display:grid;grid-template-columns:1.6fr .8fr .8fr 1.6fr auto;gap:12px;align-items:center;padding:12px 16px;border-bottom:1px solid var(--color-border-muted)}.x-catalog-row:last-child{border-bottom:0}.x-catalog-head{font-size:11px;font-weight:800;text-transform:uppercase;letter-spacing:.4px;color:var(--color-text-muted)}.x-catalog-col-name{font-size:14px;font-weight:700;color:var(--color-text)}.x-catalog-model{display:block;font-family:var(--font-mono);font-size:11.5px;font-weight:500;color:var(--color-text-muted)}.x-catalog-col-type{font-size:13px;color:var(--color-text-muted);text-transform:capitalize}.x-status-badge{display:inline-block;font-size:10.5px;font-weight:800;text-transform:uppercase;letter-spacing:.3px;padding:2px 8px;border-radius:var(--radius-pill, 999px);background:var(--color-surface-muted, var(--color-bg));border:1px solid var(--color-border);color:var(--color-text-muted)}.x-status-published{color:color-mix(in srgb,var(--color-success) 70%,black);border-color:var(--color-success)}.x-check-pending{font-size:12.5px;color:var(--color-text-muted)}.x-check-ok{font-size:12.5px;font-weight:700;color:var(--color-success)}.x-check-errors{margin:0;padding-left:16px;font-size:12px;color:color-mix(in srgb,var(--color-danger) 80%,black)}.x-check-btn{background:var(--color-accent);color:var(--color-accent-fg);border:0;border-radius:var(--radius-sm);padding:8px 12px;font-size:12.5px;font-weight:700;cursor:pointer;white-space:nowrap}.x-check-btn:disabled{opacity:.5;cursor:not-allowed}.x-catalog-empty{padding:32px 16px;text-align:center;font-size:13px;color:var(--color-text-muted)}\n"] }]
|
|
198
|
-
}] });
|
|
199
|
-
|
|
200
|
-
class CatalogModule {
|
|
201
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: CatalogModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
|
|
202
|
-
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.2.16", ngImport: i0, type: CatalogModule, declarations: [CatalogComponent], imports: [SharedModule,
|
|
203
|
-
ElementsModule,
|
|
204
|
-
WebgetsModule, i1$1.RouterModule] }); }
|
|
205
|
-
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: CatalogModule, imports: [SharedModule,
|
|
206
|
-
ElementsModule,
|
|
207
|
-
WebgetsModule,
|
|
208
|
-
RouterModule.forChild([{ path: '', component: CatalogComponent }])] }); }
|
|
209
|
-
}
|
|
210
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: CatalogModule, decorators: [{
|
|
211
|
-
type: NgModule,
|
|
212
|
-
args: [{
|
|
213
|
-
declarations: [CatalogComponent],
|
|
214
|
-
imports: [
|
|
215
|
-
SharedModule,
|
|
216
|
-
ElementsModule,
|
|
217
|
-
WebgetsModule,
|
|
218
|
-
RouterModule.forChild([{ path: '', component: CatalogComponent }]),
|
|
219
|
-
],
|
|
220
|
-
}]
|
|
221
|
-
}] });
|
|
222
|
-
|
|
223
|
-
export { CatalogModule };
|
|
224
|
-
//# sourceMappingURL=isaiahiroko-tilmart-ngx-apps-admin-catalog.module-Bvx2tVOi.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"isaiahiroko-tilmart-ngx-apps-admin-catalog.module-Bvx2tVOi.mjs","sources":["../../../projects/admin/src/lib/pages/catalog/catalog.component.ts","../../../projects/admin/src/lib/pages/catalog/catalog.module.ts"],"sourcesContent":["import { Component } from '@angular/core';\nimport { BaseDirective, using } from '@thescaffold/ngx-core';\nimport { BehaviorSubject, takeUntil, map } from 'rxjs';\n\ninterface PublishError {\n field: string;\n message: string;\n}\n\ninterface ProductRowVm {\n id: string;\n name: string;\n model: string;\n type: string;\n status: string;\n updatedAt: string;\n checking: boolean;\n checked: boolean;\n canPublish?: boolean;\n errors?: PublishError[];\n}\n\n// P1.15 — the catalogue editor's first slice. The Salama-era version of\n// this page (basket templates + a bespoke cost-price re-quote table) was\n// wholesale grocery-subscription domain and is gone; `ProductCostPrice`\n// now has its own generic CRUD screen like every other resource. What\n// replaces the bespoke half here is the thing the real catalogue actually\n// needs per row: CAT-03's publish-validation check\n// (`CatalogWorkflow.validatePublish`, P1.10) surfaced with field-level\n// errors, not a single opaque pass/fail — same \"per-row action, not just\n// a read view\" shape the old cost-price table had, new domain.\n//\n// A full dynamic per-attribute-type input grid (the more literal reading\n// of \"per-row attribute inputs\") is deliberately not attempted here — it\n// needs a form control per `AttributeDataType` (number/enum/boolean/\n// range/text) driven by whichever template the selected product's\n// category resolves to, which is real, isolated follow-up work once a\n// live category/template dataset exists to build and check it against,\n// not something to guess the shape of blind.\n@Component({\n standalone: false,\n selector: 'x-catalog',\n template: `\n <div class=\"x-page-head\">\n <div class=\"x-page-title\">Catalogue</div>\n <div class=\"x-page-subtitle\">\n Products, variants and their publish readiness (CAT-03).\n </div>\n </div>\n\n @if (products$ | async; as products) {\n <div class=\"x-catalog-table\">\n <div class=\"x-catalog-row x-catalog-head\">\n <div class=\"x-catalog-col-name\">Product</div>\n <div class=\"x-catalog-col-type\">Type</div>\n <div class=\"x-catalog-col-status\">Status</div>\n <div class=\"x-catalog-col-check\">Publish check</div>\n <div class=\"x-catalog-col-action\"></div>\n </div>\n @for (product of products; track product.id) {\n <div class=\"x-catalog-row\">\n <div class=\"x-catalog-col-name\">\n {{ product.name }}\n @if (product.model) {\n <span class=\"x-catalog-model\">{{ product.model }}</span>\n }\n </div>\n <div class=\"x-catalog-col-type\">{{ product.type }}</div>\n <div class=\"x-catalog-col-status\">\n <span class=\"x-status-badge\" [class]=\"'x-status-' + product.status\">{{\n product.status\n }}</span>\n </div>\n <div class=\"x-catalog-col-check\">\n @if (product.checking) {\n <span class=\"x-check-pending\">Checking…</span>\n } @else if (product.checked && product.canPublish) {\n <span class=\"x-check-ok\">✓ Ready to publish</span>\n } @else if (product.checked && product.errors?.length) {\n <ul class=\"x-check-errors\">\n @for (err of product.errors; track err.field) {\n <li>{{ err.message }}</li>\n }\n </ul>\n }\n </div>\n <div class=\"x-catalog-col-action\">\n <button\n type=\"button\"\n class=\"x-check-btn\"\n [disabled]=\"product.checking\"\n (click)=\"checkPublish(product)\"\n >\n Check\n </button>\n </div>\n </div>\n }\n @if (!products.length) {\n <div class=\"x-catalog-empty\">No products yet.</div>\n }\n </div>\n } @else {\n <x-element-loader></x-element-loader>\n }\n `,\n styles: [\n `\n .x-page-head {\n margin-bottom: 20px;\n }\n .x-page-title {\n font-size: 24px;\n font-weight: 800;\n color: var(--color-text);\n }\n .x-page-subtitle {\n font-size: 13.5px;\n color: var(--color-text-muted);\n margin-top: 2px;\n }\n .x-catalog-table {\n background: var(--color-surface);\n border-radius: var(--radius-lg);\n overflow: hidden;\n }\n .x-catalog-row {\n display: grid;\n grid-template-columns: 1.6fr 0.8fr 0.8fr 1.6fr auto;\n gap: 12px;\n align-items: center;\n padding: 12px 16px;\n border-bottom: 1px solid var(--color-border-muted);\n }\n .x-catalog-row:last-child {\n border-bottom: 0;\n }\n .x-catalog-head {\n font-size: 11px;\n font-weight: 800;\n text-transform: uppercase;\n letter-spacing: 0.4px;\n color: var(--color-text-muted);\n }\n .x-catalog-col-name {\n font-size: 14px;\n font-weight: 700;\n color: var(--color-text);\n }\n .x-catalog-model {\n display: block;\n font-family: var(--font-mono);\n font-size: 11.5px;\n font-weight: 500;\n color: var(--color-text-muted);\n }\n .x-catalog-col-type {\n font-size: 13px;\n color: var(--color-text-muted);\n text-transform: capitalize;\n }\n .x-status-badge {\n display: inline-block;\n font-size: 10.5px;\n font-weight: 800;\n text-transform: uppercase;\n letter-spacing: 0.3px;\n padding: 2px 8px;\n border-radius: var(--radius-pill, 999px);\n background: var(--color-surface-muted, var(--color-bg));\n border: 1px solid var(--color-border);\n color: var(--color-text-muted);\n }\n .x-status-published {\n color: color-mix(in srgb, var(--color-success) 70%, black);\n border-color: var(--color-success);\n }\n .x-check-pending {\n font-size: 12.5px;\n color: var(--color-text-muted);\n }\n .x-check-ok {\n font-size: 12.5px;\n font-weight: 700;\n color: var(--color-success);\n }\n .x-check-errors {\n margin: 0;\n padding-left: 16px;\n font-size: 12px;\n color: color-mix(in srgb, var(--color-danger) 80%, black);\n }\n .x-check-btn {\n background: var(--color-accent);\n color: var(--color-accent-fg);\n border: 0;\n border-radius: var(--radius-sm);\n padding: 8px 12px;\n font-size: 12.5px;\n font-weight: 700;\n cursor: pointer;\n white-space: nowrap;\n }\n .x-check-btn:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n }\n .x-catalog-empty {\n padding: 32px 16px;\n text-align: center;\n font-size: 13px;\n color: var(--color-text-muted);\n }\n `,\n ],\n})\nexport class CatalogComponent extends BaseDirective {\n name = 'product';\n pluralName = 'products';\n private readonly productSource = this.apiService.on('apps/commerce/product');\n\n products$ = new BehaviorSubject<ProductRowVm[] | null>(null);\n\n override init = () => {\n this.loadProducts();\n };\n\n loadProducts() {\n this.productSource\n .getMany({ page: 1, perPage: 100 })\n .pipe(\n takeUntil(this.$),\n map((response: any) =>\n (response?.data ?? []).map(\n (p: any): ProductRowVm => ({\n id: p.id,\n name: p.name,\n model: p.model,\n type: p.type,\n status: p.status,\n updatedAt: p.updatedAt,\n checking: false,\n checked: false,\n }),\n ),\n ),\n )\n .subscribe(using((rows) => this.products$.next(rows)));\n }\n\n checkPublish(product: ProductRowVm) {\n product.checking = true;\n this.productSource\n .post(`apps/commerce/product/${product.id}/validate-publish`)\n .pipe(takeUntil(this.$))\n .subscribe(\n using((response: any) => {\n product.checking = false;\n product.checked = true;\n product.canPublish = !!response?.data?.canPublish;\n product.errors = response?.data?.errors ?? [];\n }),\n );\n }\n}\n","import { NgModule } from '@angular/core';\nimport { RouterModule } from '@angular/router';\nimport { SharedModule } from '@thescaffold/ngx-core';\nimport { ElementsModule, WebgetsModule } from '@thescaffold/ngx-ui';\nimport { CatalogComponent } from './catalog.component';\n\n@NgModule({\n declarations: [CatalogComponent],\n imports: [\n SharedModule,\n ElementsModule,\n WebgetsModule,\n RouterModule.forChild([{ path: '', component: CatalogComponent }]),\n ],\n})\nexport class CatalogModule {}\n"],"names":["i1"],"mappings":"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"}
|