feat: reorganize admin settings with tabbed interface and documentation
- Reorganized admin settings into tabbed interface (General, Security, Payment Methods) - Vertical tabs on desktop, horizontal scrollable on mobile - Moved Payment Methods from separate menu to Settings tab - Fixed admin profile reuse and dashboard blocking - Fixed maintenance mode guard to use AppConfig model - Added admin auto-redirect after login (admins → /admin, users → /) - Reorganized documentation into docs/ folder structure - Created comprehensive README and documentation index - Added PWA and Web Push notifications to to-do list
This commit is contained in:
@@ -17,6 +17,7 @@ const common_1 = require("@nestjs/common");
|
||||
const auth_guard_1 = require("../auth/auth.guard");
|
||||
const admin_guard_1 = require("./guards/admin.guard");
|
||||
const admin_config_service_1 = require("./admin-config.service");
|
||||
const skip_maintenance_decorator_1 = require("../common/decorators/skip-maintenance.decorator");
|
||||
let AdminConfigController = class AdminConfigController {
|
||||
service;
|
||||
constructor(service) {
|
||||
@@ -78,6 +79,7 @@ __decorate([
|
||||
exports.AdminConfigController = AdminConfigController = __decorate([
|
||||
(0, common_1.Controller)('admin/config'),
|
||||
(0, common_1.UseGuards)(auth_guard_1.AuthGuard, admin_guard_1.AdminGuard),
|
||||
(0, skip_maintenance_decorator_1.SkipMaintenance)(),
|
||||
__metadata("design:paramtypes", [admin_config_service_1.AdminConfigService])
|
||||
], AdminConfigController);
|
||||
//# sourceMappingURL=admin-config.controller.js.map
|
||||
@@ -1 +1 @@
|
||||
{"version":3,"file":"admin-config.controller.js","sourceRoot":"","sources":["../../src/admin/admin-config.controller.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA,2CAUwB;AACxB,mDAA+C;AAC/C,sDAAkD;AAClD,iEAA4D;AAUrD,IAAM,qBAAqB,GAA3B,MAAM,qBAAqB;IACH;IAA7B,YAA6B,OAA2B;QAA3B,YAAO,GAAP,OAAO,CAAoB;IAAG,CAAC;IAG5D,OAAO,CAAoB,QAAiB;QAC1C,OAAO,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IACxC,CAAC;IAGD,aAAa;QACX,OAAO,IAAI,CAAC,OAAO,CAAC,aAAa,EAAE,CAAC;IACtC,CAAC;IAGD,OAAO,CAAe,GAAW;QAC/B,OAAO,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IACnC,CAAC;IAGD,MAAM,CACU,GAAW,EACjB,IAAS,EACV,GAAoB;QAE3B,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACzD,CAAC;IAGD,MAAM,CAAe,GAAW;QAC9B,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IAClC,CAAC;CACF,CAAA;AA/BY,sDAAqB;AAIhC;IADC,IAAA,YAAG,GAAE;IACG,WAAA,IAAA,cAAK,EAAC,UAAU,CAAC,CAAA;;;;oDAEzB;AAGD;IADC,IAAA,YAAG,EAAC,aAAa,CAAC;;;;0DAGlB;AAGD;IADC,IAAA,YAAG,EAAC,MAAM,CAAC;IACH,WAAA,IAAA,cAAK,EAAC,KAAK,CAAC,CAAA;;;;oDAEpB;AAGD;IADC,IAAA,aAAI,EAAC,MAAM,CAAC;IAEV,WAAA,IAAA,cAAK,EAAC,KAAK,CAAC,CAAA;IACZ,WAAA,IAAA,aAAI,GAAE,CAAA;IACN,WAAA,IAAA,YAAG,GAAE,CAAA;;;;mDAGP;AAGD;IADC,IAAA,eAAM,EAAC,MAAM,CAAC;IACP,WAAA,IAAA,cAAK,EAAC,KAAK,CAAC,CAAA;;;;mDAEnB;gCA9BU,qBAAqB;IAFjC,IAAA,mBAAU,EAAC,cAAc,CAAC;IAC1B,IAAA,kBAAS,EAAC,sBAAS,EAAE,wBAAU,CAAC;qCAEO,yCAAkB;GAD7C,qBAAqB,CA+BjC"}
|
||||
{"version":3,"file":"admin-config.controller.js","sourceRoot":"","sources":["../../src/admin/admin-config.controller.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA,2CAUwB;AACxB,mDAA+C;AAC/C,sDAAkD;AAClD,iEAA4D;AAC5D,gGAAkF;AAW3E,IAAM,qBAAqB,GAA3B,MAAM,qBAAqB;IACH;IAA7B,YAA6B,OAA2B;QAA3B,YAAO,GAAP,OAAO,CAAoB;IAAG,CAAC;IAG5D,OAAO,CAAoB,QAAiB;QAC1C,OAAO,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IACxC,CAAC;IAGD,aAAa;QACX,OAAO,IAAI,CAAC,OAAO,CAAC,aAAa,EAAE,CAAC;IACtC,CAAC;IAGD,OAAO,CAAe,GAAW;QAC/B,OAAO,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IACnC,CAAC;IAGD,MAAM,CACU,GAAW,EACjB,IAAS,EACV,GAAoB;QAE3B,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACzD,CAAC;IAGD,MAAM,CAAe,GAAW;QAC9B,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IAClC,CAAC;CACF,CAAA;AA/BY,sDAAqB;AAIhC;IADC,IAAA,YAAG,GAAE;IACG,WAAA,IAAA,cAAK,EAAC,UAAU,CAAC,CAAA;;;;oDAEzB;AAGD;IADC,IAAA,YAAG,EAAC,aAAa,CAAC;;;;0DAGlB;AAGD;IADC,IAAA,YAAG,EAAC,MAAM,CAAC;IACH,WAAA,IAAA,cAAK,EAAC,KAAK,CAAC,CAAA;;;;oDAEpB;AAGD;IADC,IAAA,aAAI,EAAC,MAAM,CAAC;IAEV,WAAA,IAAA,cAAK,EAAC,KAAK,CAAC,CAAA;IACZ,WAAA,IAAA,aAAI,GAAE,CAAA;IACN,WAAA,IAAA,YAAG,GAAE,CAAA;;;;mDAGP;AAGD;IADC,IAAA,eAAM,EAAC,MAAM,CAAC;IACP,WAAA,IAAA,cAAK,EAAC,KAAK,CAAC,CAAA;;;;mDAEnB;gCA9BU,qBAAqB;IAHjC,IAAA,mBAAU,EAAC,cAAc,CAAC;IAC1B,IAAA,kBAAS,EAAC,sBAAS,EAAE,wBAAU,CAAC;IAChC,IAAA,4CAAe,GAAE;qCAEsB,yCAAkB;GAD7C,qBAAqB,CA+BjC"}
|
||||
@@ -1 +1 @@
|
||||
{"version":3,"file":"admin-config.service.js","sourceRoot":"","sources":["../../src/admin/admin-config.service.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAA4C;AAC5C,6DAAyD;AAGlD,IAAM,kBAAkB,GAAxB,MAAM,kBAAkB;IACA;IAA7B,YAA6B,MAAqB;QAArB,WAAM,GAAN,MAAM,CAAe;IAAG,CAAC;IAEtD,KAAK,CAAC,OAAO,CAAC,QAAiB;QAC7B,OAAO,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,QAAQ,CAAC;YACpC,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,SAAS;YAC1C,OAAO,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE;SAC7B,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,GAAW;QACvB,OAAO,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,UAAU,CAAC;YACtC,KAAK,EAAE,EAAE,GAAG,EAAE;SACf,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,GAAW,EAAE,IAAS,EAAE,SAAiB;QACpD,OAAO,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC;YAClC,KAAK,EAAE,EAAE,GAAG,EAAE;YACd,MAAM,EAAE;gBACN,GAAG,IAAI;gBACP,SAAS;gBACT,SAAS,EAAE,IAAI,IAAI,EAAE;aACtB;YACD,MAAM,EAAE;gBACN,GAAG;gBACH,GAAG,IAAI;gBACP,SAAS;aACV;SACF,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,GAAW;QACtB,OAAO,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC;YAClC,KAAK,EAAE,EAAE,GAAG,EAAE;SACf,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,aAAa;QACjB,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,QAAQ,EAAE,CAAC;QAGvD,MAAM,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE;YAC7C,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC;gBAC1B,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC;YAC5B,CAAC;YACD,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YAClC,OAAO,GAAG,CAAC;QACb,CAAC,EAAE,EAA2B,CAAC,CAAC;QAEhC,OAAO,OAAO,CAAC;IACjB,CAAC;CACF,CAAA;AApDY,gDAAkB;6BAAlB,kBAAkB;IAD9B,IAAA,mBAAU,GAAE;qCAE0B,8BAAa;GADvC,kBAAkB,CAoD9B"}
|
||||
{"version":3,"file":"admin-config.service.js","sourceRoot":"","sources":["../../src/admin/admin-config.service.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAA4C;AAC5C,6DAAyD;AAGlD,IAAM,kBAAkB,GAAxB,MAAM,kBAAkB;IACA;IAA7B,YAA6B,MAAqB;QAArB,WAAM,GAAN,MAAM,CAAe;IAAG,CAAC;IAEtD,KAAK,CAAC,OAAO,CAAC,QAAiB;QAC7B,OAAO,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,QAAQ,CAAC;YACpC,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,SAAS;YAC1C,OAAO,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE;SAC7B,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,GAAW;QACvB,OAAO,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,UAAU,CAAC;YACtC,KAAK,EAAE,EAAE,GAAG,EAAE;SACf,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,GAAW,EAAE,IAAS,EAAE,SAAiB;QACpD,OAAO,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC;YAClC,KAAK,EAAE,EAAE,GAAG,EAAE;YACd,MAAM,EAAE;gBACN,GAAG,IAAI;gBACP,SAAS;gBACT,SAAS,EAAE,IAAI,IAAI,EAAE;aACtB;YACD,MAAM,EAAE;gBACN,GAAG;gBACH,GAAG,IAAI;gBACP,SAAS;aACV;SACF,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,GAAW;QACtB,OAAO,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC;YAClC,KAAK,EAAE,EAAE,GAAG,EAAE;SACf,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,aAAa;QACjB,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,QAAQ,EAAE,CAAC;QAGvD,MAAM,OAAO,GAAG,OAAO,CAAC,MAAM,CAC5B,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE;YACd,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC;gBAC1B,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC;YAC5B,CAAC;YACD,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YAClC,OAAO,GAAG,CAAC;QACb,CAAC,EACD,EAA2B,CAC5B,CAAC;QAEF,OAAO,OAAO,CAAC;IACjB,CAAC;CACF,CAAA;AAvDY,gDAAkB;6BAAlB,kBAAkB;IAD9B,IAAA,mBAAU,GAAE;qCAE0B,8BAAa;GADvC,kBAAkB,CAuD9B"}
|
||||
@@ -17,6 +17,7 @@ const common_1 = require("@nestjs/common");
|
||||
const auth_guard_1 = require("../auth/auth.guard");
|
||||
const admin_guard_1 = require("./guards/admin.guard");
|
||||
const admin_payment_methods_service_1 = require("./admin-payment-methods.service");
|
||||
const skip_maintenance_decorator_1 = require("../common/decorators/skip-maintenance.decorator");
|
||||
let AdminPaymentMethodsController = class AdminPaymentMethodsController {
|
||||
service;
|
||||
constructor(service) {
|
||||
@@ -87,6 +88,7 @@ __decorate([
|
||||
exports.AdminPaymentMethodsController = AdminPaymentMethodsController = __decorate([
|
||||
(0, common_1.Controller)('admin/payment-methods'),
|
||||
(0, common_1.UseGuards)(auth_guard_1.AuthGuard, admin_guard_1.AdminGuard),
|
||||
(0, skip_maintenance_decorator_1.SkipMaintenance)(),
|
||||
__metadata("design:paramtypes", [admin_payment_methods_service_1.AdminPaymentMethodsService])
|
||||
], AdminPaymentMethodsController);
|
||||
//# sourceMappingURL=admin-payment-methods.controller.js.map
|
||||
@@ -1 +1 @@
|
||||
{"version":3,"file":"admin-payment-methods.controller.js","sourceRoot":"","sources":["../../src/admin/admin-payment-methods.controller.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA,2CASwB;AACxB,mDAA+C;AAC/C,sDAAkD;AAClD,mFAA6E;AAItE,IAAM,6BAA6B,GAAnC,MAAM,6BAA6B;IACX;IAA7B,YAA6B,OAAmC;QAAnC,YAAO,GAAP,OAAO,CAA4B;IAAG,CAAC;IAGpE,OAAO;QACL,OAAO,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;IAChC,CAAC;IAGD,OAAO,CAAc,EAAU;QAC7B,OAAO,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IAClC,CAAC;IAGD,MAAM,CAAS,IAAS;QACtB,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IACnC,CAAC;IAGD,MAAM,CAAc,EAAU,EAAU,IAAS;QAC/C,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;IACvC,CAAC;IAGD,MAAM,CAAc,EAAU;QAC5B,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IACjC,CAAC;IAGD,OAAO,CAAS,IAA6B;QAC3C,OAAO,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IAC9C,CAAC;CACF,CAAA;AAhCY,sEAA6B;AAIxC;IADC,IAAA,YAAG,GAAE;;;;4DAGL;AAGD;IADC,IAAA,YAAG,EAAC,KAAK,CAAC;IACF,WAAA,IAAA,cAAK,EAAC,IAAI,CAAC,CAAA;;;;4DAEnB;AAGD;IADC,IAAA,aAAI,GAAE;IACC,WAAA,IAAA,aAAI,GAAE,CAAA;;;;2DAEb;AAGD;IADC,IAAA,YAAG,EAAC,KAAK,CAAC;IACH,WAAA,IAAA,cAAK,EAAC,IAAI,CAAC,CAAA;IAAc,WAAA,IAAA,aAAI,GAAE,CAAA;;;;2DAEtC;AAGD;IADC,IAAA,eAAM,EAAC,KAAK,CAAC;IACN,WAAA,IAAA,cAAK,EAAC,IAAI,CAAC,CAAA;;;;2DAElB;AAGD;IADC,IAAA,aAAI,EAAC,SAAS,CAAC;IACP,WAAA,IAAA,aAAI,GAAE,CAAA;;;;4DAEd;wCA/BU,6BAA6B;IAFzC,IAAA,mBAAU,EAAC,uBAAuB,CAAC;IACnC,IAAA,kBAAS,EAAC,sBAAS,EAAE,wBAAU,CAAC;qCAEO,0DAA0B;GADrD,6BAA6B,CAgCzC"}
|
||||
{"version":3,"file":"admin-payment-methods.controller.js","sourceRoot":"","sources":["../../src/admin/admin-payment-methods.controller.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA,2CASwB;AACxB,mDAA+C;AAC/C,sDAAkD;AAClD,mFAA6E;AAC7E,gGAAkF;AAK3E,IAAM,6BAA6B,GAAnC,MAAM,6BAA6B;IACX;IAA7B,YAA6B,OAAmC;QAAnC,YAAO,GAAP,OAAO,CAA4B;IAAG,CAAC;IAGpE,OAAO;QACL,OAAO,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;IAChC,CAAC;IAGD,OAAO,CAAc,EAAU;QAC7B,OAAO,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IAClC,CAAC;IAGD,MAAM,CAAS,IAAS;QACtB,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IACnC,CAAC;IAGD,MAAM,CAAc,EAAU,EAAU,IAAS;QAC/C,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;IACvC,CAAC;IAGD,MAAM,CAAc,EAAU;QAC5B,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IACjC,CAAC;IAGD,OAAO,CAAS,IAA6B;QAC3C,OAAO,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IAC9C,CAAC;CACF,CAAA;AAhCY,sEAA6B;AAIxC;IADC,IAAA,YAAG,GAAE;;;;4DAGL;AAGD;IADC,IAAA,YAAG,EAAC,KAAK,CAAC;IACF,WAAA,IAAA,cAAK,EAAC,IAAI,CAAC,CAAA;;;;4DAEnB;AAGD;IADC,IAAA,aAAI,GAAE;IACC,WAAA,IAAA,aAAI,GAAE,CAAA;;;;2DAEb;AAGD;IADC,IAAA,YAAG,EAAC,KAAK,CAAC;IACH,WAAA,IAAA,cAAK,EAAC,IAAI,CAAC,CAAA;IAAc,WAAA,IAAA,aAAI,GAAE,CAAA;;;;2DAEtC;AAGD;IADC,IAAA,eAAM,EAAC,KAAK,CAAC;IACN,WAAA,IAAA,cAAK,EAAC,IAAI,CAAC,CAAA;;;;2DAElB;AAGD;IADC,IAAA,aAAI,EAAC,SAAS,CAAC;IACP,WAAA,IAAA,aAAI,GAAE,CAAA;;;;4DAEd;wCA/BU,6BAA6B;IAHzC,IAAA,mBAAU,EAAC,uBAAuB,CAAC;IACnC,IAAA,kBAAS,EAAC,sBAAS,EAAE,wBAAU,CAAC;IAChC,IAAA,4CAAe,GAAE;qCAEsB,0DAA0B;GADrD,6BAA6B,CAgCzC"}
|
||||
@@ -16,10 +16,10 @@ export declare class AdminPaymentsController {
|
||||
subscription: ({
|
||||
plan: {
|
||||
id: string;
|
||||
currency: string;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
name: string;
|
||||
currency: string;
|
||||
slug: string;
|
||||
description: string | null;
|
||||
price: import("@prisma/client/runtime/library").Decimal;
|
||||
@@ -42,10 +42,10 @@ export declare class AdminPaymentsController {
|
||||
};
|
||||
} & {
|
||||
id: string;
|
||||
userId: string;
|
||||
status: string;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
status: string;
|
||||
userId: string;
|
||||
planId: string;
|
||||
startDate: Date;
|
||||
endDate: Date;
|
||||
@@ -56,19 +56,21 @@ export declare class AdminPaymentsController {
|
||||
}) | null;
|
||||
} & {
|
||||
id: string;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
status: string;
|
||||
method: string;
|
||||
userId: string;
|
||||
currency: string;
|
||||
amount: import("@prisma/client/runtime/library").Decimal;
|
||||
subscriptionId: string | null;
|
||||
invoiceNumber: string;
|
||||
amount: import("@prisma/client/runtime/library").Decimal;
|
||||
currency: string;
|
||||
method: string;
|
||||
tripayReference: string | null;
|
||||
tripayFee: import("@prisma/client/runtime/library").Decimal | null;
|
||||
totalAmount: import("@prisma/client/runtime/library").Decimal;
|
||||
paymentChannel: string | null;
|
||||
paymentUrl: string | null;
|
||||
qrUrl: string | null;
|
||||
status: string;
|
||||
proofImageUrl: string | null;
|
||||
transferDate: Date | null;
|
||||
verifiedBy: string | null;
|
||||
@@ -79,8 +81,6 @@ export declare class AdminPaymentsController {
|
||||
notes: string | null;
|
||||
expiresAt: Date | null;
|
||||
paidAt: Date | null;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
})[]>;
|
||||
getPendingCount(): Promise<number>;
|
||||
getMonthlyRevenue(): Promise<{
|
||||
@@ -97,10 +97,10 @@ export declare class AdminPaymentsController {
|
||||
subscription: ({
|
||||
plan: {
|
||||
id: string;
|
||||
currency: string;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
name: string;
|
||||
currency: string;
|
||||
slug: string;
|
||||
description: string | null;
|
||||
price: import("@prisma/client/runtime/library").Decimal;
|
||||
@@ -123,10 +123,10 @@ export declare class AdminPaymentsController {
|
||||
};
|
||||
} & {
|
||||
id: string;
|
||||
userId: string;
|
||||
status: string;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
status: string;
|
||||
userId: string;
|
||||
planId: string;
|
||||
startDate: Date;
|
||||
endDate: Date;
|
||||
@@ -137,19 +137,21 @@ export declare class AdminPaymentsController {
|
||||
}) | null;
|
||||
} & {
|
||||
id: string;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
status: string;
|
||||
method: string;
|
||||
userId: string;
|
||||
currency: string;
|
||||
amount: import("@prisma/client/runtime/library").Decimal;
|
||||
subscriptionId: string | null;
|
||||
invoiceNumber: string;
|
||||
amount: import("@prisma/client/runtime/library").Decimal;
|
||||
currency: string;
|
||||
method: string;
|
||||
tripayReference: string | null;
|
||||
tripayFee: import("@prisma/client/runtime/library").Decimal | null;
|
||||
totalAmount: import("@prisma/client/runtime/library").Decimal;
|
||||
paymentChannel: string | null;
|
||||
paymentUrl: string | null;
|
||||
qrUrl: string | null;
|
||||
status: string;
|
||||
proofImageUrl: string | null;
|
||||
transferDate: Date | null;
|
||||
verifiedBy: string | null;
|
||||
@@ -160,24 +162,24 @@ export declare class AdminPaymentsController {
|
||||
notes: string | null;
|
||||
expiresAt: Date | null;
|
||||
paidAt: Date | null;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
}) | null>;
|
||||
verify(id: string, req: RequestWithUser): Promise<{
|
||||
id: string;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
status: string;
|
||||
method: string;
|
||||
userId: string;
|
||||
currency: string;
|
||||
amount: import("@prisma/client/runtime/library").Decimal;
|
||||
subscriptionId: string | null;
|
||||
invoiceNumber: string;
|
||||
amount: import("@prisma/client/runtime/library").Decimal;
|
||||
currency: string;
|
||||
method: string;
|
||||
tripayReference: string | null;
|
||||
tripayFee: import("@prisma/client/runtime/library").Decimal | null;
|
||||
totalAmount: import("@prisma/client/runtime/library").Decimal;
|
||||
paymentChannel: string | null;
|
||||
paymentUrl: string | null;
|
||||
qrUrl: string | null;
|
||||
status: string;
|
||||
proofImageUrl: string | null;
|
||||
transferDate: Date | null;
|
||||
verifiedBy: string | null;
|
||||
@@ -188,26 +190,26 @@ export declare class AdminPaymentsController {
|
||||
notes: string | null;
|
||||
expiresAt: Date | null;
|
||||
paidAt: Date | null;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
}>;
|
||||
reject(id: string, req: RequestWithUser, body: {
|
||||
reason: string;
|
||||
}): Promise<{
|
||||
id: string;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
status: string;
|
||||
method: string;
|
||||
userId: string;
|
||||
currency: string;
|
||||
amount: import("@prisma/client/runtime/library").Decimal;
|
||||
subscriptionId: string | null;
|
||||
invoiceNumber: string;
|
||||
amount: import("@prisma/client/runtime/library").Decimal;
|
||||
currency: string;
|
||||
method: string;
|
||||
tripayReference: string | null;
|
||||
tripayFee: import("@prisma/client/runtime/library").Decimal | null;
|
||||
totalAmount: import("@prisma/client/runtime/library").Decimal;
|
||||
paymentChannel: string | null;
|
||||
paymentUrl: string | null;
|
||||
qrUrl: string | null;
|
||||
status: string;
|
||||
proofImageUrl: string | null;
|
||||
transferDate: Date | null;
|
||||
verifiedBy: string | null;
|
||||
@@ -218,8 +220,6 @@ export declare class AdminPaymentsController {
|
||||
notes: string | null;
|
||||
expiresAt: Date | null;
|
||||
paidAt: Date | null;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
}>;
|
||||
}
|
||||
export {};
|
||||
|
||||
@@ -17,6 +17,7 @@ const common_1 = require("@nestjs/common");
|
||||
const auth_guard_1 = require("../auth/auth.guard");
|
||||
const admin_guard_1 = require("./guards/admin.guard");
|
||||
const admin_payments_service_1 = require("./admin-payments.service");
|
||||
const skip_maintenance_decorator_1 = require("../common/decorators/skip-maintenance.decorator");
|
||||
let AdminPaymentsController = class AdminPaymentsController {
|
||||
service;
|
||||
constructor(service) {
|
||||
@@ -88,6 +89,7 @@ __decorate([
|
||||
exports.AdminPaymentsController = AdminPaymentsController = __decorate([
|
||||
(0, common_1.Controller)('admin/payments'),
|
||||
(0, common_1.UseGuards)(auth_guard_1.AuthGuard, admin_guard_1.AdminGuard),
|
||||
(0, skip_maintenance_decorator_1.SkipMaintenance)(),
|
||||
__metadata("design:paramtypes", [admin_payments_service_1.AdminPaymentsService])
|
||||
], AdminPaymentsController);
|
||||
//# sourceMappingURL=admin-payments.controller.js.map
|
||||
@@ -1 +1 @@
|
||||
{"version":3,"file":"admin-payments.controller.js","sourceRoot":"","sources":["../../src/admin/admin-payments.controller.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA,2CASwB;AACxB,mDAA+C;AAC/C,sDAAkD;AAClD,qEAAgE;AAUzD,IAAM,uBAAuB,GAA7B,MAAM,uBAAuB;IACL;IAA7B,YAA6B,OAA6B;QAA7B,YAAO,GAAP,OAAO,CAAsB;IAAG,CAAC;IAG9D,OAAO,CAAkB,MAAe;QACtC,OAAO,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IACtC,CAAC;IAGD,eAAe;QACb,OAAO,IAAI,CAAC,OAAO,CAAC,eAAe,EAAE,CAAC;IACxC,CAAC;IAGD,iBAAiB;QACf,OAAO,IAAI,CAAC,OAAO,CAAC,iBAAiB,EAAE,CAAC;IAC1C,CAAC;IAGD,OAAO,CAAc,EAAU;QAC7B,OAAO,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IAClC,CAAC;IAGD,MAAM,CAAc,EAAU,EAAS,GAAoB;QACzD,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAClD,CAAC;IAGD,MAAM,CACS,EAAU,EAChB,GAAoB,EACnB,IAAwB;QAEhC,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;IAC/D,CAAC;CACF,CAAA;AApCY,0DAAuB;AAIlC;IADC,IAAA,YAAG,GAAE;IACG,WAAA,IAAA,cAAK,EAAC,QAAQ,CAAC,CAAA;;;;sDAEvB;AAGD;IADC,IAAA,YAAG,EAAC,eAAe,CAAC;;;;8DAGpB;AAGD;IADC,IAAA,YAAG,EAAC,iBAAiB,CAAC;;;;gEAGtB;AAGD;IADC,IAAA,YAAG,EAAC,KAAK,CAAC;IACF,WAAA,IAAA,cAAK,EAAC,IAAI,CAAC,CAAA;;;;sDAEnB;AAGD;IADC,IAAA,aAAI,EAAC,YAAY,CAAC;IACX,WAAA,IAAA,cAAK,EAAC,IAAI,CAAC,CAAA;IAAc,WAAA,IAAA,YAAG,GAAE,CAAA;;;;qDAErC;AAGD;IADC,IAAA,aAAI,EAAC,YAAY,CAAC;IAEhB,WAAA,IAAA,cAAK,EAAC,IAAI,CAAC,CAAA;IACX,WAAA,IAAA,YAAG,GAAE,CAAA;IACL,WAAA,IAAA,aAAI,GAAE,CAAA;;;;qDAGR;kCAnCU,uBAAuB;IAFnC,IAAA,mBAAU,EAAC,gBAAgB,CAAC;IAC5B,IAAA,kBAAS,EAAC,sBAAS,EAAE,wBAAU,CAAC;qCAEO,6CAAoB;GAD/C,uBAAuB,CAoCnC"}
|
||||
{"version":3,"file":"admin-payments.controller.js","sourceRoot":"","sources":["../../src/admin/admin-payments.controller.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA,2CASwB;AACxB,mDAA+C;AAC/C,sDAAkD;AAClD,qEAAgE;AAChE,gGAAkF;AAW3E,IAAM,uBAAuB,GAA7B,MAAM,uBAAuB;IACL;IAA7B,YAA6B,OAA6B;QAA7B,YAAO,GAAP,OAAO,CAAsB;IAAG,CAAC;IAG9D,OAAO,CAAkB,MAAe;QACtC,OAAO,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IACtC,CAAC;IAGD,eAAe;QACb,OAAO,IAAI,CAAC,OAAO,CAAC,eAAe,EAAE,CAAC;IACxC,CAAC;IAGD,iBAAiB;QACf,OAAO,IAAI,CAAC,OAAO,CAAC,iBAAiB,EAAE,CAAC;IAC1C,CAAC;IAGD,OAAO,CAAc,EAAU;QAC7B,OAAO,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IAClC,CAAC;IAGD,MAAM,CAAc,EAAU,EAAS,GAAoB;QACzD,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAClD,CAAC;IAGD,MAAM,CACS,EAAU,EAChB,GAAoB,EACnB,IAAwB;QAEhC,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;IAC/D,CAAC;CACF,CAAA;AApCY,0DAAuB;AAIlC;IADC,IAAA,YAAG,GAAE;IACG,WAAA,IAAA,cAAK,EAAC,QAAQ,CAAC,CAAA;;;;sDAEvB;AAGD;IADC,IAAA,YAAG,EAAC,eAAe,CAAC;;;;8DAGpB;AAGD;IADC,IAAA,YAAG,EAAC,iBAAiB,CAAC;;;;gEAGtB;AAGD;IADC,IAAA,YAAG,EAAC,KAAK,CAAC;IACF,WAAA,IAAA,cAAK,EAAC,IAAI,CAAC,CAAA;;;;sDAEnB;AAGD;IADC,IAAA,aAAI,EAAC,YAAY,CAAC;IACX,WAAA,IAAA,cAAK,EAAC,IAAI,CAAC,CAAA;IAAc,WAAA,IAAA,YAAG,GAAE,CAAA;;;;qDAErC;AAGD;IADC,IAAA,aAAI,EAAC,YAAY,CAAC;IAEhB,WAAA,IAAA,cAAK,EAAC,IAAI,CAAC,CAAA;IACX,WAAA,IAAA,YAAG,GAAE,CAAA;IACL,WAAA,IAAA,aAAI,GAAE,CAAA;;;;qDAGR;kCAnCU,uBAAuB;IAHnC,IAAA,mBAAU,EAAC,gBAAgB,CAAC;IAC5B,IAAA,kBAAS,EAAC,sBAAS,EAAE,wBAAU,CAAC;IAChC,IAAA,4CAAe,GAAE;qCAEsB,6CAAoB;GAD/C,uBAAuB,CAoCnC"}
|
||||
60
apps/api/dist/admin/admin-payments.service.d.ts
vendored
60
apps/api/dist/admin/admin-payments.service.d.ts
vendored
@@ -11,10 +11,10 @@ export declare class AdminPaymentsService {
|
||||
subscription: ({
|
||||
plan: {
|
||||
id: string;
|
||||
currency: string;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
name: string;
|
||||
currency: string;
|
||||
slug: string;
|
||||
description: string | null;
|
||||
price: import("@prisma/client/runtime/library").Decimal;
|
||||
@@ -37,10 +37,10 @@ export declare class AdminPaymentsService {
|
||||
};
|
||||
} & {
|
||||
id: string;
|
||||
userId: string;
|
||||
status: string;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
status: string;
|
||||
userId: string;
|
||||
planId: string;
|
||||
startDate: Date;
|
||||
endDate: Date;
|
||||
@@ -51,19 +51,21 @@ export declare class AdminPaymentsService {
|
||||
}) | null;
|
||||
} & {
|
||||
id: string;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
status: string;
|
||||
method: string;
|
||||
userId: string;
|
||||
currency: string;
|
||||
amount: import("@prisma/client/runtime/library").Decimal;
|
||||
subscriptionId: string | null;
|
||||
invoiceNumber: string;
|
||||
amount: import("@prisma/client/runtime/library").Decimal;
|
||||
currency: string;
|
||||
method: string;
|
||||
tripayReference: string | null;
|
||||
tripayFee: import("@prisma/client/runtime/library").Decimal | null;
|
||||
totalAmount: import("@prisma/client/runtime/library").Decimal;
|
||||
paymentChannel: string | null;
|
||||
paymentUrl: string | null;
|
||||
qrUrl: string | null;
|
||||
status: string;
|
||||
proofImageUrl: string | null;
|
||||
transferDate: Date | null;
|
||||
verifiedBy: string | null;
|
||||
@@ -74,8 +76,6 @@ export declare class AdminPaymentsService {
|
||||
notes: string | null;
|
||||
expiresAt: Date | null;
|
||||
paidAt: Date | null;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
})[]>;
|
||||
findOne(id: string): Promise<({
|
||||
user: {
|
||||
@@ -86,10 +86,10 @@ export declare class AdminPaymentsService {
|
||||
subscription: ({
|
||||
plan: {
|
||||
id: string;
|
||||
currency: string;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
name: string;
|
||||
currency: string;
|
||||
slug: string;
|
||||
description: string | null;
|
||||
price: import("@prisma/client/runtime/library").Decimal;
|
||||
@@ -112,10 +112,10 @@ export declare class AdminPaymentsService {
|
||||
};
|
||||
} & {
|
||||
id: string;
|
||||
userId: string;
|
||||
status: string;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
status: string;
|
||||
userId: string;
|
||||
planId: string;
|
||||
startDate: Date;
|
||||
endDate: Date;
|
||||
@@ -126,19 +126,21 @@ export declare class AdminPaymentsService {
|
||||
}) | null;
|
||||
} & {
|
||||
id: string;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
status: string;
|
||||
method: string;
|
||||
userId: string;
|
||||
currency: string;
|
||||
amount: import("@prisma/client/runtime/library").Decimal;
|
||||
subscriptionId: string | null;
|
||||
invoiceNumber: string;
|
||||
amount: import("@prisma/client/runtime/library").Decimal;
|
||||
currency: string;
|
||||
method: string;
|
||||
tripayReference: string | null;
|
||||
tripayFee: import("@prisma/client/runtime/library").Decimal | null;
|
||||
totalAmount: import("@prisma/client/runtime/library").Decimal;
|
||||
paymentChannel: string | null;
|
||||
paymentUrl: string | null;
|
||||
qrUrl: string | null;
|
||||
status: string;
|
||||
proofImageUrl: string | null;
|
||||
transferDate: Date | null;
|
||||
verifiedBy: string | null;
|
||||
@@ -149,24 +151,24 @@ export declare class AdminPaymentsService {
|
||||
notes: string | null;
|
||||
expiresAt: Date | null;
|
||||
paidAt: Date | null;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
}) | null>;
|
||||
verify(id: string, adminUserId: string): Promise<{
|
||||
id: string;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
status: string;
|
||||
method: string;
|
||||
userId: string;
|
||||
currency: string;
|
||||
amount: import("@prisma/client/runtime/library").Decimal;
|
||||
subscriptionId: string | null;
|
||||
invoiceNumber: string;
|
||||
amount: import("@prisma/client/runtime/library").Decimal;
|
||||
currency: string;
|
||||
method: string;
|
||||
tripayReference: string | null;
|
||||
tripayFee: import("@prisma/client/runtime/library").Decimal | null;
|
||||
totalAmount: import("@prisma/client/runtime/library").Decimal;
|
||||
paymentChannel: string | null;
|
||||
paymentUrl: string | null;
|
||||
qrUrl: string | null;
|
||||
status: string;
|
||||
proofImageUrl: string | null;
|
||||
transferDate: Date | null;
|
||||
verifiedBy: string | null;
|
||||
@@ -177,24 +179,24 @@ export declare class AdminPaymentsService {
|
||||
notes: string | null;
|
||||
expiresAt: Date | null;
|
||||
paidAt: Date | null;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
}>;
|
||||
reject(id: string, adminUserId: string, reason: string): Promise<{
|
||||
id: string;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
status: string;
|
||||
method: string;
|
||||
userId: string;
|
||||
currency: string;
|
||||
amount: import("@prisma/client/runtime/library").Decimal;
|
||||
subscriptionId: string | null;
|
||||
invoiceNumber: string;
|
||||
amount: import("@prisma/client/runtime/library").Decimal;
|
||||
currency: string;
|
||||
method: string;
|
||||
tripayReference: string | null;
|
||||
tripayFee: import("@prisma/client/runtime/library").Decimal | null;
|
||||
totalAmount: import("@prisma/client/runtime/library").Decimal;
|
||||
paymentChannel: string | null;
|
||||
paymentUrl: string | null;
|
||||
qrUrl: string | null;
|
||||
status: string;
|
||||
proofImageUrl: string | null;
|
||||
transferDate: Date | null;
|
||||
verifiedBy: string | null;
|
||||
@@ -205,8 +207,6 @@ export declare class AdminPaymentsService {
|
||||
notes: string | null;
|
||||
expiresAt: Date | null;
|
||||
paidAt: Date | null;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
}>;
|
||||
getPendingCount(): Promise<number>;
|
||||
getMonthlyRevenue(): Promise<{
|
||||
|
||||
15
apps/api/dist/admin/admin-payments.service.js
vendored
15
apps/api/dist/admin/admin-payments.service.js
vendored
@@ -123,7 +123,20 @@ let AdminPaymentsService = class AdminPaymentsService {
|
||||
},
|
||||
});
|
||||
const monthlyData = {};
|
||||
const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
|
||||
const months = [
|
||||
'Jan',
|
||||
'Feb',
|
||||
'Mar',
|
||||
'Apr',
|
||||
'May',
|
||||
'Jun',
|
||||
'Jul',
|
||||
'Aug',
|
||||
'Sep',
|
||||
'Oct',
|
||||
'Nov',
|
||||
'Dec',
|
||||
];
|
||||
payments.forEach((payment) => {
|
||||
if (payment.paidAt) {
|
||||
const date = new Date(payment.paidAt);
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"version":3,"file":"admin-payments.service.js","sourceRoot":"","sources":["../../src/admin/admin-payments.service.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAA4C;AAC5C,6DAAyD;AAGlD,IAAM,oBAAoB,GAA1B,MAAM,oBAAoB;IACF;IAA7B,YAA6B,MAAqB;QAArB,WAAM,GAAN,MAAM,CAAe;IAAG,CAAC;IAEtD,KAAK,CAAC,OAAO,CAAC,MAAe;QAC3B,OAAO,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC;YAClC,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,SAAS;YACtC,OAAO,EAAE;gBACP,IAAI,EAAE;oBACJ,MAAM,EAAE;wBACN,EAAE,EAAE,IAAI;wBACR,KAAK,EAAE,IAAI;wBACX,IAAI,EAAE,IAAI;qBACX;iBACF;gBACD,YAAY,EAAE;oBACZ,OAAO,EAAE;wBACP,IAAI,EAAE,IAAI;qBACX;iBACF;aACF;YACD,OAAO,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE;SAC/B,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,EAAU;QACtB,OAAO,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC;YACpC,KAAK,EAAE,EAAE,EAAE,EAAE;YACb,OAAO,EAAE;gBACP,IAAI,EAAE;oBACJ,MAAM,EAAE;wBACN,EAAE,EAAE,IAAI;wBACR,KAAK,EAAE,IAAI;wBACX,IAAI,EAAE,IAAI;qBACX;iBACF;gBACD,YAAY,EAAE;oBACZ,OAAO,EAAE;wBACP,IAAI,EAAE,IAAI;qBACX;iBACF;aACF;SACF,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,EAAU,EAAE,WAAmB;QAC1C,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC;YACnD,KAAK,EAAE,EAAE,EAAE,EAAE;YACb,OAAO,EAAE,EAAE,YAAY,EAAE,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE;SACvD,CAAC,CAAC;QAEH,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,MAAM,IAAI,KAAK,CAAC,mBAAmB,CAAC,CAAC;QACvC,CAAC;QAGD,MAAM,cAAc,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC;YACtD,KAAK,EAAE,EAAE,EAAE,EAAE;YACb,IAAI,EAAE;gBACJ,MAAM,EAAE,MAAM;gBACd,UAAU,EAAE,WAAW;gBACvB,UAAU,EAAE,IAAI,IAAI,EAAE;gBACtB,MAAM,EAAE,IAAI,IAAI,EAAE;aACnB;SACF,CAAC,CAAC;QAGH,IAAI,OAAO,CAAC,cAAc,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC;YACnD,MAAM,IAAI,GAAG,OAAO,CAAC,YAAY,CAAC,IAAI,CAAC;YACvC,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC;YACvB,MAAM,OAAO,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC;YAE9B,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;gBACtB,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,YAAY,CAAC,CAAC;YACzD,CAAC;YAED,MAAM,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,MAAM,CAAC;gBACpC,KAAK,EAAE,EAAE,EAAE,EAAE,OAAO,CAAC,cAAc,EAAE;gBACrC,IAAI,EAAE;oBACJ,MAAM,EAAE,QAAQ;oBAChB,SAAS,EAAE,GAAG;oBACd,OAAO,EAAE,IAAI,CAAC,YAAY,KAAK,UAAU,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,OAAO;iBAC7E;aACF,CAAC,CAAC;QACL,CAAC;QAED,OAAO,cAAc,CAAC;IACxB,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,EAAU,EAAE,WAAmB,EAAE,MAAc;QAC1D,OAAO,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC;YAChC,KAAK,EAAE,EAAE,EAAE,EAAE;YACb,IAAI,EAAE;gBACJ,MAAM,EAAE,UAAU;gBAClB,UAAU,EAAE,WAAW;gBACvB,UAAU,EAAE,IAAI,IAAI,EAAE;gBACtB,eAAe,EAAE,MAAM;aACxB;SACF,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,eAAe;QACnB,OAAO,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC;YAC/B,KAAK,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE;SAC7B,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,iBAAiB;QAErB,MAAM,YAAY,GAAG,IAAI,IAAI,EAAE,CAAC;QAChC,YAAY,CAAC,QAAQ,CAAC,YAAY,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC,CAAC;QAEnD,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC;YAClD,KAAK,EAAE;gBACL,MAAM,EAAE,MAAM;gBACd,MAAM,EAAE;oBACN,GAAG,EAAE,YAAY;iBAClB;aACF;YACD,MAAM,EAAE;gBACN,MAAM,EAAE,IAAI;gBACZ,MAAM,EAAE,IAAI;aACb;SACF,CAAC,CAAC;QAGH,MAAM,WAAW,GAA0D,EAAE,CAAC;QAC9E,MAAM,MAAM,GAAG,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;QAEpG,QAAQ,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;YAC3B,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;gBACnB,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;gBACtC,MAAM,QAAQ,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,IAAI,IAAI,CAAC,WAAW,EAAE,EAAE,CAAC;gBAEpE,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,EAAE,CAAC;oBAC3B,WAAW,CAAC,QAAQ,CAAC,GAAG,EAAE,OAAO,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;gBACnD,CAAC;gBAED,WAAW,CAAC,QAAQ,CAAC,CAAC,OAAO,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;gBACxD,WAAW,CAAC,QAAQ,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC;YACnC,CAAC;QACH,CAAC,CAAC,CAAC;QAGH,MAAM,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC;aACvC,GAAG,CAAC,CAAC,CAAC,KAAK,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC;YACvB,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;YAC1B,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,KAAK,EAAE,IAAI,CAAC,KAAK;SAClB,CAAC,CAAC;aACF,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;QAEb,OAAO,MAAM,CAAC;IAChB,CAAC;CACF,CAAA;AAzJY,oDAAoB;+BAApB,oBAAoB;IADhC,IAAA,mBAAU,GAAE;qCAE0B,8BAAa;GADvC,oBAAoB,CAyJhC"}
|
||||
{"version":3,"file":"admin-payments.service.js","sourceRoot":"","sources":["../../src/admin/admin-payments.service.ts"],"names":[],"mappings":"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"}
|
||||
30
apps/api/dist/admin/admin-plans.controller.d.ts
vendored
30
apps/api/dist/admin/admin-plans.controller.d.ts
vendored
@@ -8,11 +8,13 @@ export declare class AdminPlansController {
|
||||
};
|
||||
} & {
|
||||
id: string;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
name: string;
|
||||
currency: string;
|
||||
slug: string;
|
||||
description: string | null;
|
||||
price: import("@prisma/client/runtime/library").Decimal;
|
||||
currency: string;
|
||||
durationType: string;
|
||||
durationDays: number | null;
|
||||
trialDays: number;
|
||||
@@ -29,8 +31,6 @@ export declare class AdminPlansController {
|
||||
maxTeamMembers: number | null;
|
||||
apiEnabled: boolean;
|
||||
apiRateLimit: number | null;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
})[]>;
|
||||
findOne(id: string): Promise<({
|
||||
_count: {
|
||||
@@ -38,11 +38,13 @@ export declare class AdminPlansController {
|
||||
};
|
||||
} & {
|
||||
id: string;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
name: string;
|
||||
currency: string;
|
||||
slug: string;
|
||||
description: string | null;
|
||||
price: import("@prisma/client/runtime/library").Decimal;
|
||||
currency: string;
|
||||
durationType: string;
|
||||
durationDays: number | null;
|
||||
trialDays: number;
|
||||
@@ -59,16 +61,16 @@ export declare class AdminPlansController {
|
||||
maxTeamMembers: number | null;
|
||||
apiEnabled: boolean;
|
||||
apiRateLimit: number | null;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
}) | null>;
|
||||
create(data: any): Promise<{
|
||||
id: string;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
name: string;
|
||||
currency: string;
|
||||
slug: string;
|
||||
description: string | null;
|
||||
price: import("@prisma/client/runtime/library").Decimal;
|
||||
currency: string;
|
||||
durationType: string;
|
||||
durationDays: number | null;
|
||||
trialDays: number;
|
||||
@@ -85,16 +87,16 @@ export declare class AdminPlansController {
|
||||
maxTeamMembers: number | null;
|
||||
apiEnabled: boolean;
|
||||
apiRateLimit: number | null;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
}>;
|
||||
update(id: string, data: any): Promise<{
|
||||
id: string;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
name: string;
|
||||
currency: string;
|
||||
slug: string;
|
||||
description: string | null;
|
||||
price: import("@prisma/client/runtime/library").Decimal;
|
||||
currency: string;
|
||||
durationType: string;
|
||||
durationDays: number | null;
|
||||
trialDays: number;
|
||||
@@ -111,8 +113,6 @@ export declare class AdminPlansController {
|
||||
maxTeamMembers: number | null;
|
||||
apiEnabled: boolean;
|
||||
apiRateLimit: number | null;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
}>;
|
||||
delete(id: string): Promise<{
|
||||
success: boolean;
|
||||
@@ -120,11 +120,13 @@ export declare class AdminPlansController {
|
||||
action: string;
|
||||
plan: {
|
||||
id: string;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
name: string;
|
||||
currency: string;
|
||||
slug: string;
|
||||
description: string | null;
|
||||
price: import("@prisma/client/runtime/library").Decimal;
|
||||
currency: string;
|
||||
durationType: string;
|
||||
durationDays: number | null;
|
||||
trialDays: number;
|
||||
@@ -141,8 +143,6 @@ export declare class AdminPlansController {
|
||||
maxTeamMembers: number | null;
|
||||
apiEnabled: boolean;
|
||||
apiRateLimit: number | null;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
};
|
||||
} | {
|
||||
success: boolean;
|
||||
|
||||
@@ -17,6 +17,7 @@ const common_1 = require("@nestjs/common");
|
||||
const auth_guard_1 = require("../auth/auth.guard");
|
||||
const admin_guard_1 = require("./guards/admin.guard");
|
||||
const admin_plans_service_1 = require("./admin-plans.service");
|
||||
const skip_maintenance_decorator_1 = require("../common/decorators/skip-maintenance.decorator");
|
||||
let AdminPlansController = class AdminPlansController {
|
||||
plansService;
|
||||
constructor(plansService) {
|
||||
@@ -87,6 +88,7 @@ __decorate([
|
||||
exports.AdminPlansController = AdminPlansController = __decorate([
|
||||
(0, common_1.Controller)('admin/plans'),
|
||||
(0, common_1.UseGuards)(auth_guard_1.AuthGuard, admin_guard_1.AdminGuard),
|
||||
(0, skip_maintenance_decorator_1.SkipMaintenance)(),
|
||||
__metadata("design:paramtypes", [admin_plans_service_1.AdminPlansService])
|
||||
], AdminPlansController);
|
||||
//# sourceMappingURL=admin-plans.controller.js.map
|
||||
@@ -1 +1 @@
|
||||
{"version":3,"file":"admin-plans.controller.js","sourceRoot":"","sources":["../../src/admin/admin-plans.controller.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA,2CASwB;AACxB,mDAA+C;AAC/C,sDAAkD;AAClD,+DAA0D;AAInD,IAAM,oBAAoB,GAA1B,MAAM,oBAAoB;IACF;IAA7B,YAA6B,YAA+B;QAA/B,iBAAY,GAAZ,YAAY,CAAmB;IAAG,CAAC;IAGhE,OAAO;QACL,OAAO,IAAI,CAAC,YAAY,CAAC,OAAO,EAAE,CAAC;IACrC,CAAC;IAGD,OAAO,CAAc,EAAU;QAC7B,OAAO,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IACvC,CAAC;IAGD,MAAM,CAAS,IAAS;QACtB,OAAO,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IACxC,CAAC;IAGD,MAAM,CAAc,EAAU,EAAU,IAAS;QAC/C,OAAO,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;IAC5C,CAAC;IAGD,MAAM,CAAc,EAAU;QAC5B,OAAO,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IACtC,CAAC;IAGD,OAAO,CAAS,IAA2B;QACzC,OAAO,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACjD,CAAC;CACF,CAAA;AAhCY,oDAAoB;AAI/B;IADC,IAAA,YAAG,GAAE;;;;mDAGL;AAGD;IADC,IAAA,YAAG,EAAC,KAAK,CAAC;IACF,WAAA,IAAA,cAAK,EAAC,IAAI,CAAC,CAAA;;;;mDAEnB;AAGD;IADC,IAAA,aAAI,GAAE;IACC,WAAA,IAAA,aAAI,GAAE,CAAA;;;;kDAEb;AAGD;IADC,IAAA,YAAG,EAAC,KAAK,CAAC;IACH,WAAA,IAAA,cAAK,EAAC,IAAI,CAAC,CAAA;IAAc,WAAA,IAAA,aAAI,GAAE,CAAA;;;;kDAEtC;AAGD;IADC,IAAA,eAAM,EAAC,KAAK,CAAC;IACN,WAAA,IAAA,cAAK,EAAC,IAAI,CAAC,CAAA;;;;kDAElB;AAGD;IADC,IAAA,aAAI,EAAC,SAAS,CAAC;IACP,WAAA,IAAA,aAAI,GAAE,CAAA;;;;mDAEd;+BA/BU,oBAAoB;IAFhC,IAAA,mBAAU,EAAC,aAAa,CAAC;IACzB,IAAA,kBAAS,EAAC,sBAAS,EAAE,wBAAU,CAAC;qCAEY,uCAAiB;GADjD,oBAAoB,CAgChC"}
|
||||
{"version":3,"file":"admin-plans.controller.js","sourceRoot":"","sources":["../../src/admin/admin-plans.controller.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA,2CASwB;AACxB,mDAA+C;AAC/C,sDAAkD;AAClD,+DAA0D;AAC1D,gGAAkF;AAK3E,IAAM,oBAAoB,GAA1B,MAAM,oBAAoB;IACF;IAA7B,YAA6B,YAA+B;QAA/B,iBAAY,GAAZ,YAAY,CAAmB;IAAG,CAAC;IAGhE,OAAO;QACL,OAAO,IAAI,CAAC,YAAY,CAAC,OAAO,EAAE,CAAC;IACrC,CAAC;IAGD,OAAO,CAAc,EAAU;QAC7B,OAAO,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IACvC,CAAC;IAGD,MAAM,CAAS,IAAS;QACtB,OAAO,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IACxC,CAAC;IAGD,MAAM,CAAc,EAAU,EAAU,IAAS;QAC/C,OAAO,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;IAC5C,CAAC;IAGD,MAAM,CAAc,EAAU;QAC5B,OAAO,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IACtC,CAAC;IAGD,OAAO,CAAS,IAA2B;QACzC,OAAO,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACjD,CAAC;CACF,CAAA;AAhCY,oDAAoB;AAI/B;IADC,IAAA,YAAG,GAAE;;;;mDAGL;AAGD;IADC,IAAA,YAAG,EAAC,KAAK,CAAC;IACF,WAAA,IAAA,cAAK,EAAC,IAAI,CAAC,CAAA;;;;mDAEnB;AAGD;IADC,IAAA,aAAI,GAAE;IACC,WAAA,IAAA,aAAI,GAAE,CAAA;;;;kDAEb;AAGD;IADC,IAAA,YAAG,EAAC,KAAK,CAAC;IACH,WAAA,IAAA,cAAK,EAAC,IAAI,CAAC,CAAA;IAAc,WAAA,IAAA,aAAI,GAAE,CAAA;;;;kDAEtC;AAGD;IADC,IAAA,eAAM,EAAC,KAAK,CAAC;IACN,WAAA,IAAA,cAAK,EAAC,IAAI,CAAC,CAAA;;;;kDAElB;AAGD;IADC,IAAA,aAAI,EAAC,SAAS,CAAC;IACP,WAAA,IAAA,aAAI,GAAE,CAAA;;;;mDAEd;+BA/BU,oBAAoB;IAHhC,IAAA,mBAAU,EAAC,aAAa,CAAC;IACzB,IAAA,kBAAS,EAAC,sBAAS,EAAE,wBAAU,CAAC;IAChC,IAAA,4CAAe,GAAE;qCAE2B,uCAAiB;GADjD,oBAAoB,CAgChC"}
|
||||
30
apps/api/dist/admin/admin-plans.service.d.ts
vendored
30
apps/api/dist/admin/admin-plans.service.d.ts
vendored
@@ -8,11 +8,13 @@ export declare class AdminPlansService {
|
||||
};
|
||||
} & {
|
||||
id: string;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
name: string;
|
||||
currency: string;
|
||||
slug: string;
|
||||
description: string | null;
|
||||
price: import("@prisma/client/runtime/library").Decimal;
|
||||
currency: string;
|
||||
durationType: string;
|
||||
durationDays: number | null;
|
||||
trialDays: number;
|
||||
@@ -29,8 +31,6 @@ export declare class AdminPlansService {
|
||||
maxTeamMembers: number | null;
|
||||
apiEnabled: boolean;
|
||||
apiRateLimit: number | null;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
})[]>;
|
||||
findOne(id: string): Promise<({
|
||||
_count: {
|
||||
@@ -38,11 +38,13 @@ export declare class AdminPlansService {
|
||||
};
|
||||
} & {
|
||||
id: string;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
name: string;
|
||||
currency: string;
|
||||
slug: string;
|
||||
description: string | null;
|
||||
price: import("@prisma/client/runtime/library").Decimal;
|
||||
currency: string;
|
||||
durationType: string;
|
||||
durationDays: number | null;
|
||||
trialDays: number;
|
||||
@@ -59,16 +61,16 @@ export declare class AdminPlansService {
|
||||
maxTeamMembers: number | null;
|
||||
apiEnabled: boolean;
|
||||
apiRateLimit: number | null;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
}) | null>;
|
||||
create(data: any): Promise<{
|
||||
id: string;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
name: string;
|
||||
currency: string;
|
||||
slug: string;
|
||||
description: string | null;
|
||||
price: import("@prisma/client/runtime/library").Decimal;
|
||||
currency: string;
|
||||
durationType: string;
|
||||
durationDays: number | null;
|
||||
trialDays: number;
|
||||
@@ -85,16 +87,16 @@ export declare class AdminPlansService {
|
||||
maxTeamMembers: number | null;
|
||||
apiEnabled: boolean;
|
||||
apiRateLimit: number | null;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
}>;
|
||||
update(id: string, data: any): Promise<{
|
||||
id: string;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
name: string;
|
||||
currency: string;
|
||||
slug: string;
|
||||
description: string | null;
|
||||
price: import("@prisma/client/runtime/library").Decimal;
|
||||
currency: string;
|
||||
durationType: string;
|
||||
durationDays: number | null;
|
||||
trialDays: number;
|
||||
@@ -111,8 +113,6 @@ export declare class AdminPlansService {
|
||||
maxTeamMembers: number | null;
|
||||
apiEnabled: boolean;
|
||||
apiRateLimit: number | null;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
}>;
|
||||
delete(id: string): Promise<{
|
||||
success: boolean;
|
||||
@@ -120,11 +120,13 @@ export declare class AdminPlansService {
|
||||
action: string;
|
||||
plan: {
|
||||
id: string;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
name: string;
|
||||
currency: string;
|
||||
slug: string;
|
||||
description: string | null;
|
||||
price: import("@prisma/client/runtime/library").Decimal;
|
||||
currency: string;
|
||||
durationType: string;
|
||||
durationDays: number | null;
|
||||
trialDays: number;
|
||||
@@ -141,8 +143,6 @@ export declare class AdminPlansService {
|
||||
maxTeamMembers: number | null;
|
||||
apiEnabled: boolean;
|
||||
apiRateLimit: number | null;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
};
|
||||
} | {
|
||||
success: boolean;
|
||||
|
||||
28
apps/api/dist/admin/admin-users.controller.d.ts
vendored
28
apps/api/dist/admin/admin-users.controller.d.ts
vendored
@@ -177,4 +177,32 @@ export declare class AdminUsersController {
|
||||
cancelledAt: Date | null;
|
||||
cancellationReason: string | null;
|
||||
}>;
|
||||
create(body: {
|
||||
email: string;
|
||||
password: string;
|
||||
name?: string;
|
||||
role?: string;
|
||||
}): Promise<{
|
||||
id: string;
|
||||
email: string;
|
||||
createdAt: Date;
|
||||
emailVerified: boolean;
|
||||
name: string | null;
|
||||
role: string;
|
||||
}>;
|
||||
update(id: string, body: {
|
||||
email?: string;
|
||||
name?: string;
|
||||
role?: string;
|
||||
}): Promise<{
|
||||
id: string;
|
||||
email: string;
|
||||
createdAt: Date;
|
||||
emailVerified: boolean;
|
||||
name: string | null;
|
||||
role: string;
|
||||
}>;
|
||||
delete(id: string): Promise<{
|
||||
message: string;
|
||||
}>;
|
||||
}
|
||||
|
||||
33
apps/api/dist/admin/admin-users.controller.js
vendored
33
apps/api/dist/admin/admin-users.controller.js
vendored
@@ -17,6 +17,7 @@ const common_1 = require("@nestjs/common");
|
||||
const auth_guard_1 = require("../auth/auth.guard");
|
||||
const admin_guard_1 = require("./guards/admin.guard");
|
||||
const admin_users_service_1 = require("./admin-users.service");
|
||||
const skip_maintenance_decorator_1 = require("../common/decorators/skip-maintenance.decorator");
|
||||
let AdminUsersController = class AdminUsersController {
|
||||
service;
|
||||
constructor(service) {
|
||||
@@ -43,6 +44,15 @@ let AdminUsersController = class AdminUsersController {
|
||||
grantProAccess(id, body) {
|
||||
return this.service.grantProAccess(id, body.planSlug, body.durationDays);
|
||||
}
|
||||
create(body) {
|
||||
return this.service.create(body);
|
||||
}
|
||||
update(id, body) {
|
||||
return this.service.update(id, body);
|
||||
}
|
||||
delete(id) {
|
||||
return this.service.delete(id);
|
||||
}
|
||||
};
|
||||
exports.AdminUsersController = AdminUsersController;
|
||||
__decorate([
|
||||
@@ -96,9 +106,32 @@ __decorate([
|
||||
__metadata("design:paramtypes", [String, Object]),
|
||||
__metadata("design:returntype", void 0)
|
||||
], AdminUsersController.prototype, "grantProAccess", null);
|
||||
__decorate([
|
||||
(0, common_1.Post)(),
|
||||
__param(0, (0, common_1.Body)()),
|
||||
__metadata("design:type", Function),
|
||||
__metadata("design:paramtypes", [Object]),
|
||||
__metadata("design:returntype", void 0)
|
||||
], AdminUsersController.prototype, "create", null);
|
||||
__decorate([
|
||||
(0, common_1.Put)(':id'),
|
||||
__param(0, (0, common_1.Param)('id')),
|
||||
__param(1, (0, common_1.Body)()),
|
||||
__metadata("design:type", Function),
|
||||
__metadata("design:paramtypes", [String, Object]),
|
||||
__metadata("design:returntype", void 0)
|
||||
], AdminUsersController.prototype, "update", null);
|
||||
__decorate([
|
||||
(0, common_1.Delete)(':id'),
|
||||
__param(0, (0, common_1.Param)('id')),
|
||||
__metadata("design:type", Function),
|
||||
__metadata("design:paramtypes", [String]),
|
||||
__metadata("design:returntype", void 0)
|
||||
], AdminUsersController.prototype, "delete", null);
|
||||
exports.AdminUsersController = AdminUsersController = __decorate([
|
||||
(0, common_1.Controller)('admin/users'),
|
||||
(0, common_1.UseGuards)(auth_guard_1.AuthGuard, admin_guard_1.AdminGuard),
|
||||
(0, skip_maintenance_decorator_1.SkipMaintenance)(),
|
||||
__metadata("design:paramtypes", [admin_users_service_1.AdminUsersService])
|
||||
], AdminUsersController);
|
||||
//# sourceMappingURL=admin-users.controller.js.map
|
||||
@@ -1 +1 @@
|
||||
{"version":3,"file":"admin-users.controller.js","sourceRoot":"","sources":["../../src/admin/admin-users.controller.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA,2CASwB;AACxB,mDAA+C;AAC/C,sDAAkD;AAClD,+DAA0D;AAInD,IAAM,oBAAoB,GAA1B,MAAM,oBAAoB;IACF;IAA7B,YAA6B,OAA0B;QAA1B,YAAO,GAAP,OAAO,CAAmB;IAAG,CAAC;IAG3D,OAAO,CAAkB,MAAe;QACtC,OAAO,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IACtC,CAAC;IAGD,QAAQ;QACN,OAAO,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC;IACjC,CAAC;IAGD,OAAO,CAAc,EAAU;QAC7B,OAAO,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IAClC,CAAC;IAGD,UAAU,CAAc,EAAU,EAAU,IAAsB;QAChE,OAAO,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;IAChD,CAAC;IAGD,OAAO,CAAc,EAAU,EAAU,IAAwB;QAC/D,OAAO,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;IAC/C,CAAC;IAGD,SAAS,CAAc,EAAU;QAC/B,OAAO,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;IACpC,CAAC;IAGD,cAAc,CACC,EAAU,EACf,IAAgD;QAExD,OAAO,IAAI,CAAC,OAAO,CAAC,cAAc,CAAC,EAAE,EAAE,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC;IAC3E,CAAC;CACF,CAAA;AAxCY,oDAAoB;AAI/B;IADC,IAAA,YAAG,GAAE;IACG,WAAA,IAAA,cAAK,EAAC,QAAQ,CAAC,CAAA;;;;mDAEvB;AAGD;IADC,IAAA,YAAG,EAAC,OAAO,CAAC;;;;oDAGZ;AAGD;IADC,IAAA,YAAG,EAAC,KAAK,CAAC;IACF,WAAA,IAAA,cAAK,EAAC,IAAI,CAAC,CAAA;;;;mDAEnB;AAGD;IADC,IAAA,YAAG,EAAC,UAAU,CAAC;IACJ,WAAA,IAAA,cAAK,EAAC,IAAI,CAAC,CAAA;IAAc,WAAA,IAAA,aAAI,GAAE,CAAA;;;;sDAE1C;AAGD;IADC,IAAA,aAAI,EAAC,aAAa,CAAC;IACX,WAAA,IAAA,cAAK,EAAC,IAAI,CAAC,CAAA;IAAc,WAAA,IAAA,aAAI,GAAE,CAAA;;;;mDAEvC;AAGD;IADC,IAAA,aAAI,EAAC,eAAe,CAAC;IACX,WAAA,IAAA,cAAK,EAAC,IAAI,CAAC,CAAA;;;;qDAErB;AAGD;IADC,IAAA,aAAI,EAAC,eAAe,CAAC;IAEnB,WAAA,IAAA,cAAK,EAAC,IAAI,CAAC,CAAA;IACX,WAAA,IAAA,aAAI,GAAE,CAAA;;;;0DAGR;+BAvCU,oBAAoB;IAFhC,IAAA,mBAAU,EAAC,aAAa,CAAC;IACzB,IAAA,kBAAS,EAAC,sBAAS,EAAE,wBAAU,CAAC;qCAEO,uCAAiB;GAD5C,oBAAoB,CAwChC"}
|
||||
{"version":3,"file":"admin-users.controller.js","sourceRoot":"","sources":["../../src/admin/admin-users.controller.ts"],"names":[],"mappings":"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"}
|
||||
28
apps/api/dist/admin/admin-users.service.d.ts
vendored
28
apps/api/dist/admin/admin-users.service.d.ts
vendored
@@ -170,4 +170,32 @@ export declare class AdminUsersService {
|
||||
activeSubscriptions: number;
|
||||
suspendedUsers: number;
|
||||
}>;
|
||||
create(data: {
|
||||
email: string;
|
||||
password: string;
|
||||
name?: string;
|
||||
role?: string;
|
||||
}): Promise<{
|
||||
id: string;
|
||||
email: string;
|
||||
createdAt: Date;
|
||||
emailVerified: boolean;
|
||||
name: string | null;
|
||||
role: string;
|
||||
}>;
|
||||
update(id: string, data: {
|
||||
email?: string;
|
||||
name?: string;
|
||||
role?: string;
|
||||
}): Promise<{
|
||||
id: string;
|
||||
email: string;
|
||||
createdAt: Date;
|
||||
emailVerified: boolean;
|
||||
name: string | null;
|
||||
role: string;
|
||||
}>;
|
||||
delete(id: string): Promise<{
|
||||
message: string;
|
||||
}>;
|
||||
}
|
||||
|
||||
104
apps/api/dist/admin/admin-users.service.js
vendored
104
apps/api/dist/admin/admin-users.service.js
vendored
@@ -1,10 +1,43 @@
|
||||
"use strict";
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
||||
}
|
||||
Object.defineProperty(o, k2, desc);
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
||||
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
||||
}) : function(o, v) {
|
||||
o["default"] = v;
|
||||
});
|
||||
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
||||
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
||||
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
||||
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
||||
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
||||
};
|
||||
var __importStar = (this && this.__importStar) || (function () {
|
||||
var ownKeys = function(o) {
|
||||
ownKeys = Object.getOwnPropertyNames || function (o) {
|
||||
var ar = [];
|
||||
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
||||
return ar;
|
||||
};
|
||||
return ownKeys(o);
|
||||
};
|
||||
return function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
||||
__setModuleDefault(result, mod);
|
||||
return result;
|
||||
};
|
||||
})();
|
||||
var __metadata = (this && this.__metadata) || function (k, v) {
|
||||
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
||||
};
|
||||
@@ -12,6 +45,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.AdminUsersService = void 0;
|
||||
const common_1 = require("@nestjs/common");
|
||||
const prisma_service_1 = require("../prisma/prisma.service");
|
||||
const bcrypt = __importStar(require("bcrypt"));
|
||||
let AdminUsersService = class AdminUsersService {
|
||||
prisma;
|
||||
constructor(prisma) {
|
||||
@@ -139,6 +173,76 @@ let AdminUsersService = class AdminUsersService {
|
||||
suspendedUsers,
|
||||
};
|
||||
}
|
||||
async create(data) {
|
||||
const existing = await this.prisma.user.findUnique({
|
||||
where: { email: data.email },
|
||||
});
|
||||
if (existing) {
|
||||
throw new common_1.ConflictException('User with this email already exists');
|
||||
}
|
||||
const hashedPassword = await bcrypt.hash(data.password, 10);
|
||||
return this.prisma.user.create({
|
||||
data: {
|
||||
email: data.email,
|
||||
passwordHash: hashedPassword,
|
||||
name: data.name || null,
|
||||
role: data.role || 'user',
|
||||
emailVerified: true,
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
email: true,
|
||||
name: true,
|
||||
role: true,
|
||||
emailVerified: true,
|
||||
createdAt: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
async update(id, data) {
|
||||
const user = await this.prisma.user.findUnique({
|
||||
where: { id },
|
||||
});
|
||||
if (!user) {
|
||||
throw new common_1.NotFoundException('User not found');
|
||||
}
|
||||
if (data.email && data.email !== user.email) {
|
||||
const existing = await this.prisma.user.findUnique({
|
||||
where: { email: data.email },
|
||||
});
|
||||
if (existing) {
|
||||
throw new common_1.ConflictException('Email already in use');
|
||||
}
|
||||
}
|
||||
return this.prisma.user.update({
|
||||
where: { id },
|
||||
data: {
|
||||
...(data.email && { email: data.email }),
|
||||
...(data.name !== undefined && { name: data.name }),
|
||||
...(data.role && { role: data.role }),
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
email: true,
|
||||
name: true,
|
||||
role: true,
|
||||
emailVerified: true,
|
||||
createdAt: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
async delete(id) {
|
||||
const user = await this.prisma.user.findUnique({
|
||||
where: { id },
|
||||
});
|
||||
if (!user) {
|
||||
throw new common_1.NotFoundException('User not found');
|
||||
}
|
||||
await this.prisma.user.delete({
|
||||
where: { id },
|
||||
});
|
||||
return { message: 'User deleted successfully' };
|
||||
}
|
||||
};
|
||||
exports.AdminUsersService = AdminUsersService;
|
||||
exports.AdminUsersService = AdminUsersService = __decorate([
|
||||
|
||||
File diff suppressed because one or more lines are too long
9
apps/api/dist/app.module.js
vendored
9
apps/api/dist/app.module.js
vendored
@@ -42,6 +42,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.AppModule = void 0;
|
||||
const common_1 = require("@nestjs/common");
|
||||
const config_1 = require("@nestjs/config");
|
||||
const core_1 = require("@nestjs/core");
|
||||
const path = __importStar(require("path"));
|
||||
const prisma_module_1 = require("./prisma/prisma.module");
|
||||
const auth_module_1 = require("./auth/auth.module");
|
||||
@@ -52,6 +53,7 @@ const transactions_module_1 = require("./transactions/transactions.module");
|
||||
const categories_module_1 = require("./categories/categories.module");
|
||||
const otp_module_1 = require("./otp/otp.module");
|
||||
const admin_module_1 = require("./admin/admin.module");
|
||||
const maintenance_guard_1 = require("./common/guards/maintenance.guard");
|
||||
let AppModule = class AppModule {
|
||||
};
|
||||
exports.AppModule = AppModule;
|
||||
@@ -75,7 +77,12 @@ exports.AppModule = AppModule = __decorate([
|
||||
admin_module_1.AdminModule,
|
||||
],
|
||||
controllers: [health_controller_1.HealthController],
|
||||
providers: [],
|
||||
providers: [
|
||||
{
|
||||
provide: core_1.APP_GUARD,
|
||||
useClass: maintenance_guard_1.MaintenanceGuard,
|
||||
},
|
||||
],
|
||||
})
|
||||
], AppModule);
|
||||
//# sourceMappingURL=app.module.js.map
|
||||
2
apps/api/dist/app.module.js.map
vendored
2
apps/api/dist/app.module.js.map
vendored
@@ -1 +1 @@
|
||||
{"version":3,"file":"app.module.js","sourceRoot":"","sources":["../src/app.module.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,2CAAwC;AACxC,2CAA8C;AAC9C,2CAA6B;AAC7B,0DAAsD;AACtD,oDAAgD;AAChD,kEAA8D;AAC9D,uDAAmD;AACnD,6DAAyD;AACzD,4EAAwE;AACxE,sEAAkE;AAClE,iDAA6C;AAC7C,uDAAmD;AAuB5C,IAAM,SAAS,GAAf,MAAM,SAAS;CAAG,CAAA;AAAZ,8BAAS;oBAAT,SAAS;IArBrB,IAAA,eAAM,EAAC;QACN,OAAO,EAAE;YACP,qBAAY,CAAC,OAAO,CAAC;gBACnB,QAAQ,EAAE,IAAI;gBACd,WAAW,EAAE;oBACX,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,MAAM,CAAC;oBACnC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,YAAY,CAAC;iBAC1C;aACF,CAAC;YACF,4BAAY;YACZ,wBAAU;YACV,0BAAW;YACX,8BAAa;YACb,wCAAkB;YAClB,oCAAgB;YAChB,sBAAS;YACT,0BAAW;SACZ;QACD,WAAW,EAAE,CAAC,oCAAgB,CAAC;QAC/B,SAAS,EAAE,EAAE;KACd,CAAC;GACW,SAAS,CAAG"}
|
||||
{"version":3,"file":"app.module.js","sourceRoot":"","sources":["../src/app.module.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,2CAAwC;AACxC,2CAA8C;AAC9C,uCAAyC;AACzC,2CAA6B;AAC7B,0DAAsD;AACtD,oDAAgD;AAChD,kEAA8D;AAC9D,uDAAmD;AACnD,6DAAyD;AACzD,4EAAwE;AACxE,sEAAkE;AAClE,iDAA6C;AAC7C,uDAAmD;AACnD,yEAAqE;AA4B9D,IAAM,SAAS,GAAf,MAAM,SAAS;CAAG,CAAA;AAAZ,8BAAS;oBAAT,SAAS;IA1BrB,IAAA,eAAM,EAAC;QACN,OAAO,EAAE;YACP,qBAAY,CAAC,OAAO,CAAC;gBACnB,QAAQ,EAAE,IAAI;gBACd,WAAW,EAAE;oBACX,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,MAAM,CAAC;oBACnC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,YAAY,CAAC;iBAC1C;aACF,CAAC;YACF,4BAAY;YACZ,wBAAU;YACV,0BAAW;YACX,8BAAa;YACb,wCAAkB;YAClB,oCAAgB;YAChB,sBAAS;YACT,0BAAW;SACZ;QACD,WAAW,EAAE,CAAC,oCAAgB,CAAC;QAC/B,SAAS,EAAE;YACT;gBACE,OAAO,EAAE,gBAAS;gBAClB,QAAQ,EAAE,oCAAgB;aAC3B;SACF;KACF,CAAC;GACW,SAAS,CAAG"}
|
||||
6
apps/api/dist/auth/auth.controller.js
vendored
6
apps/api/dist/auth/auth.controller.js
vendored
@@ -17,6 +17,7 @@ const common_1 = require("@nestjs/common");
|
||||
const auth_guard_1 = require("./auth.guard");
|
||||
const passport_1 = require("@nestjs/passport");
|
||||
const auth_service_1 = require("./auth.service");
|
||||
const skip_maintenance_decorator_1 = require("../common/decorators/skip-maintenance.decorator");
|
||||
let AuthController = class AuthController {
|
||||
authService;
|
||||
constructor(authService) {
|
||||
@@ -53,6 +54,7 @@ let AuthController = class AuthController {
|
||||
exports.AuthController = AuthController;
|
||||
__decorate([
|
||||
(0, common_1.Post)('register'),
|
||||
(0, skip_maintenance_decorator_1.SkipMaintenance)(),
|
||||
__param(0, (0, common_1.Body)()),
|
||||
__metadata("design:type", Function),
|
||||
__metadata("design:paramtypes", [Object]),
|
||||
@@ -60,6 +62,7 @@ __decorate([
|
||||
], AuthController.prototype, "register", null);
|
||||
__decorate([
|
||||
(0, common_1.Post)('login'),
|
||||
(0, skip_maintenance_decorator_1.SkipMaintenance)(),
|
||||
__param(0, (0, common_1.Body)()),
|
||||
__metadata("design:type", Function),
|
||||
__metadata("design:paramtypes", [Object]),
|
||||
@@ -67,6 +70,7 @@ __decorate([
|
||||
], AuthController.prototype, "login", null);
|
||||
__decorate([
|
||||
(0, common_1.Post)('verify-otp'),
|
||||
(0, skip_maintenance_decorator_1.SkipMaintenance)(),
|
||||
__param(0, (0, common_1.Body)()),
|
||||
__metadata("design:type", Function),
|
||||
__metadata("design:paramtypes", [Object]),
|
||||
@@ -74,6 +78,7 @@ __decorate([
|
||||
], AuthController.prototype, "verifyOtp", null);
|
||||
__decorate([
|
||||
(0, common_1.Get)('google'),
|
||||
(0, skip_maintenance_decorator_1.SkipMaintenance)(),
|
||||
(0, common_1.UseGuards)((0, passport_1.AuthGuard)('google')),
|
||||
__metadata("design:type", Function),
|
||||
__metadata("design:paramtypes", []),
|
||||
@@ -81,6 +86,7 @@ __decorate([
|
||||
], AuthController.prototype, "googleAuth", null);
|
||||
__decorate([
|
||||
(0, common_1.Get)('google/callback'),
|
||||
(0, skip_maintenance_decorator_1.SkipMaintenance)(),
|
||||
(0, common_1.UseGuards)((0, passport_1.AuthGuard)('google')),
|
||||
__param(0, (0, common_1.Req)()),
|
||||
__param(1, (0, common_1.Res)()),
|
||||
|
||||
2
apps/api/dist/auth/auth.controller.js.map
vendored
2
apps/api/dist/auth/auth.controller.js.map
vendored
@@ -1 +1 @@
|
||||
{"version":3,"file":"auth.controller.js","sourceRoot":"","sources":["../../src/auth/auth.controller.ts"],"names":[],"mappings":"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"}
|
||||
{"version":3,"file":"auth.controller.js","sourceRoot":"","sources":["../../src/auth/auth.controller.ts"],"names":[],"mappings":"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"}
|
||||
2
apps/api/dist/auth/auth.module.js
vendored
2
apps/api/dist/auth/auth.module.js
vendored
@@ -32,7 +32,7 @@ exports.AuthModule = AuthModule = __decorate([
|
||||
],
|
||||
controllers: [auth_controller_1.AuthController],
|
||||
providers: [auth_service_1.AuthService, jwt_strategy_1.JwtStrategy, google_strategy_1.GoogleStrategy],
|
||||
exports: [auth_service_1.AuthService],
|
||||
exports: [auth_service_1.AuthService, jwt_1.JwtModule],
|
||||
})
|
||||
], AuthModule);
|
||||
//# sourceMappingURL=auth.module.js.map
|
||||
2
apps/api/dist/auth/auth.module.js.map
vendored
2
apps/api/dist/auth/auth.module.js.map
vendored
@@ -1 +1 @@
|
||||
{"version":3,"file":"auth.module.js","sourceRoot":"","sources":["../../src/auth/auth.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAoD;AACpD,qCAAwC;AACxC,+CAAkD;AAClD,uDAAmD;AACnD,iDAA6C;AAC7C,iDAA6C;AAC7C,uDAAmD;AACnD,2DAAuD;AACvD,kDAA8C;AAgBvC,IAAM,UAAU,GAAhB,MAAM,UAAU;CAAG,CAAA;AAAb,gCAAU;qBAAV,UAAU;IAdtB,IAAA,eAAM,EAAC;QACN,OAAO,EAAE;YACP,4BAAY;YACZ,yBAAc;YACd,IAAA,mBAAU,EAAC,GAAG,EAAE,CAAC,sBAAS,CAAC;YAC3B,eAAS,CAAC,QAAQ,CAAC;gBACjB,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,iBAAiB;gBACnD,WAAW,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE;aACjC,CAAC;SACH;QACD,WAAW,EAAE,CAAC,gCAAc,CAAC;QAC7B,SAAS,EAAE,CAAC,0BAAW,EAAE,0BAAW,EAAE,gCAAc,CAAC;QACrD,OAAO,EAAE,CAAC,0BAAW,CAAC;KACvB,CAAC;GACW,UAAU,CAAG"}
|
||||
{"version":3,"file":"auth.module.js","sourceRoot":"","sources":["../../src/auth/auth.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAoD;AACpD,qCAAwC;AACxC,+CAAkD;AAClD,uDAAmD;AACnD,iDAA6C;AAC7C,iDAA6C;AAC7C,uDAAmD;AACnD,2DAAuD;AACvD,kDAA8C;AAgBvC,IAAM,UAAU,GAAhB,MAAM,UAAU;CAAG,CAAA;AAAb,gCAAU;qBAAV,UAAU;IAdtB,IAAA,eAAM,EAAC;QACN,OAAO,EAAE;YACP,4BAAY;YACZ,yBAAc;YACd,IAAA,mBAAU,EAAC,GAAG,EAAE,CAAC,sBAAS,CAAC;YAC3B,eAAS,CAAC,QAAQ,CAAC;gBACjB,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,iBAAiB;gBACnD,WAAW,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE;aACjC,CAAC;SACH;QACD,WAAW,EAAE,CAAC,gCAAc,CAAC;QAC7B,SAAS,EAAE,CAAC,0BAAW,EAAE,0BAAW,EAAE,gCAAc,CAAC;QACrD,OAAO,EAAE,CAAC,0BAAW,EAAE,eAAS,CAAC;KAClC,CAAC;GACW,UAAU,CAAG"}
|
||||
2
apps/api/dist/auth/jwt.strategy.js
vendored
2
apps/api/dist/auth/jwt.strategy.js
vendored
@@ -25,7 +25,7 @@ let JwtStrategy = class JwtStrategy extends (0, passport_1.PassportStrategy)(pas
|
||||
return {
|
||||
userId: payload.sub,
|
||||
email: payload.email,
|
||||
role: payload.role || 'user'
|
||||
role: payload.role || 'user',
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"version":3,"file":"categories.controller.js","sourceRoot":"","sources":["../../src/categories/categories.controller.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA,2CAA4F;AAC5F,yEAAqE;AACrE,+EAA0E;AAC1E,mDAA+C;AAUxC,IAAM,oBAAoB,GAA1B,MAAM,oBAAoB;IACF;IAA7B,YAA6B,iBAAoC;QAApC,sBAAiB,GAAjB,iBAAiB,CAAmB;IAAG,CAAC;IAGrE,MAAM,CAAQ,GAAoB,EAAU,iBAAoC;QAC9E,OAAO,IAAI,CAAC,iBAAiB,CAAC,MAAM,CAAC;YACnC,GAAG,iBAAiB;YACpB,MAAM,EAAE,GAAG,CAAC,IAAI,CAAC,MAAM;SACxB,CAAC,CAAC;IACL,CAAC;IAGD,OAAO,CAAQ,GAAoB;QACjC,OAAO,IAAI,CAAC,iBAAiB,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACzD,CAAC;IAGD,MAAM,CAAQ,GAAoB,EAAe,EAAU;QACzD,OAAO,IAAI,CAAC,iBAAiB,CAAC,MAAM,CAAC,EAAE,EAAE,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAC5D,CAAC;CACF,CAAA;AApBY,oDAAoB;AAI/B;IADC,IAAA,aAAI,GAAE;IACC,WAAA,IAAA,YAAG,GAAE,CAAA;IAAwB,WAAA,IAAA,aAAI,GAAE,CAAA;;6CAAoB,uCAAiB;;kDAK/E;AAGD;IADC,IAAA,YAAG,GAAE;IACG,WAAA,IAAA,YAAG,GAAE,CAAA;;;;mDAEb;AAGD;IADC,IAAA,eAAM,EAAC,KAAK,CAAC;IACN,WAAA,IAAA,YAAG,GAAE,CAAA;IAAwB,WAAA,IAAA,cAAK,EAAC,IAAI,CAAC,CAAA;;;;kDAE/C;+BAnBU,oBAAoB;IAFhC,IAAA,mBAAU,EAAC,YAAY,CAAC;IACxB,IAAA,kBAAS,EAAC,sBAAS,CAAC;qCAE6B,sCAAiB;GADtD,oBAAoB,CAoBhC"}
|
||||
{"version":3,"file":"categories.controller.js","sourceRoot":"","sources":["../../src/categories/categories.controller.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA,2CASwB;AACxB,yEAAqE;AACrE,+EAA0E;AAC1E,mDAA+C;AAUxC,IAAM,oBAAoB,GAA1B,MAAM,oBAAoB;IACF;IAA7B,YAA6B,iBAAoC;QAApC,sBAAiB,GAAjB,iBAAiB,CAAmB;IAAG,CAAC;IAGrE,MAAM,CACG,GAAoB,EACnB,iBAAoC;QAE5C,OAAO,IAAI,CAAC,iBAAiB,CAAC,MAAM,CAAC;YACnC,GAAG,iBAAiB;YACpB,MAAM,EAAE,GAAG,CAAC,IAAI,CAAC,MAAM;SACxB,CAAC,CAAC;IACL,CAAC;IAGD,OAAO,CAAQ,GAAoB;QACjC,OAAO,IAAI,CAAC,iBAAiB,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACzD,CAAC;IAGD,MAAM,CAAQ,GAAoB,EAAe,EAAU;QACzD,OAAO,IAAI,CAAC,iBAAiB,CAAC,MAAM,CAAC,EAAE,EAAE,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAC5D,CAAC;CACF,CAAA;AAvBY,oDAAoB;AAI/B;IADC,IAAA,aAAI,GAAE;IAEJ,WAAA,IAAA,YAAG,GAAE,CAAA;IACL,WAAA,IAAA,aAAI,GAAE,CAAA;;6CAAoB,uCAAiB;;kDAM7C;AAGD;IADC,IAAA,YAAG,GAAE;IACG,WAAA,IAAA,YAAG,GAAE,CAAA;;;;mDAEb;AAGD;IADC,IAAA,eAAM,EAAC,KAAK,CAAC;IACN,WAAA,IAAA,YAAG,GAAE,CAAA;IAAwB,WAAA,IAAA,cAAK,EAAC,IAAI,CAAC,CAAA;;;;kDAE/C;+BAtBU,oBAAoB;IAFhC,IAAA,mBAAU,EAAC,YAAY,CAAC;IACxB,IAAA,kBAAS,EAAC,sBAAS,CAAC;qCAE6B,sCAAiB;GADtD,oBAAoB,CAuBhC"}
|
||||
1
apps/api/dist/common/decorators/skip-maintenance.decorator.d.ts
vendored
Normal file
1
apps/api/dist/common/decorators/skip-maintenance.decorator.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
||||
export declare const SkipMaintenance: () => import("@nestjs/common").CustomDecorator<string>;
|
||||
7
apps/api/dist/common/decorators/skip-maintenance.decorator.js
vendored
Normal file
7
apps/api/dist/common/decorators/skip-maintenance.decorator.js
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.SkipMaintenance = void 0;
|
||||
const common_1 = require("@nestjs/common");
|
||||
const SkipMaintenance = () => (0, common_1.SetMetadata)('skipMaintenance', true);
|
||||
exports.SkipMaintenance = SkipMaintenance;
|
||||
//# sourceMappingURL=skip-maintenance.decorator.js.map
|
||||
1
apps/api/dist/common/decorators/skip-maintenance.decorator.js.map
vendored
Normal file
1
apps/api/dist/common/decorators/skip-maintenance.decorator.js.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"skip-maintenance.decorator.js","sourceRoot":"","sources":["../../../src/common/decorators/skip-maintenance.decorator.ts"],"names":[],"mappings":";;;AAAA,2CAA6C;AAEtC,MAAM,eAAe,GAAG,GAAG,EAAE,CAAC,IAAA,oBAAW,EAAC,iBAAiB,EAAE,IAAI,CAAC,CAAC;AAA7D,QAAA,eAAe,mBAA8C"}
|
||||
11
apps/api/dist/common/guards/maintenance.guard.d.ts
vendored
Normal file
11
apps/api/dist/common/guards/maintenance.guard.d.ts
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
import { CanActivate, ExecutionContext } from '@nestjs/common';
|
||||
import { Reflector } from '@nestjs/core';
|
||||
import { PrismaService } from '../../prisma/prisma.service';
|
||||
import { JwtService } from '@nestjs/jwt';
|
||||
export declare class MaintenanceGuard implements CanActivate {
|
||||
private reflector;
|
||||
private prisma;
|
||||
private jwtService;
|
||||
constructor(reflector: Reflector, prisma: PrismaService, jwtService: JwtService);
|
||||
canActivate(context: ExecutionContext): Promise<boolean>;
|
||||
}
|
||||
71
apps/api/dist/common/guards/maintenance.guard.js
vendored
Normal file
71
apps/api/dist/common/guards/maintenance.guard.js
vendored
Normal file
@@ -0,0 +1,71 @@
|
||||
"use strict";
|
||||
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
||||
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
||||
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
||||
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
||||
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
||||
};
|
||||
var __metadata = (this && this.__metadata) || function (k, v) {
|
||||
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.MaintenanceGuard = void 0;
|
||||
const common_1 = require("@nestjs/common");
|
||||
const core_1 = require("@nestjs/core");
|
||||
const prisma_service_1 = require("../../prisma/prisma.service");
|
||||
const jwt_1 = require("@nestjs/jwt");
|
||||
let MaintenanceGuard = class MaintenanceGuard {
|
||||
reflector;
|
||||
prisma;
|
||||
jwtService;
|
||||
constructor(reflector, prisma, jwtService) {
|
||||
this.reflector = reflector;
|
||||
this.prisma = prisma;
|
||||
this.jwtService = jwtService;
|
||||
}
|
||||
async canActivate(context) {
|
||||
const isExempt = this.reflector.get('skipMaintenance', context.getHandler());
|
||||
const isControllerExempt = this.reflector.get('skipMaintenance', context.getClass());
|
||||
if (isExempt || isControllerExempt) {
|
||||
return true;
|
||||
}
|
||||
const maintenanceConfig = await this.prisma.appConfig.findUnique({
|
||||
where: { key: 'maintenance_mode' },
|
||||
});
|
||||
const isMaintenanceMode = maintenanceConfig?.value === 'true';
|
||||
if (!isMaintenanceMode) {
|
||||
return true;
|
||||
}
|
||||
const request = context.switchToHttp().getRequest();
|
||||
const authHeader = request.headers.authorization;
|
||||
if (authHeader && authHeader.startsWith('Bearer ')) {
|
||||
try {
|
||||
const token = authHeader.substring(7);
|
||||
const payload = this.jwtService.verify(token);
|
||||
if (payload.role === 'admin') {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
}
|
||||
}
|
||||
const messageConfig = await this.prisma.appConfig.findUnique({
|
||||
where: { key: 'maintenance_message' },
|
||||
});
|
||||
const message = messageConfig?.value ||
|
||||
'System is under maintenance. Please try again later.';
|
||||
throw new common_1.ServiceUnavailableException({
|
||||
statusCode: 503,
|
||||
message: message,
|
||||
maintenanceMode: true,
|
||||
});
|
||||
}
|
||||
};
|
||||
exports.MaintenanceGuard = MaintenanceGuard;
|
||||
exports.MaintenanceGuard = MaintenanceGuard = __decorate([
|
||||
(0, common_1.Injectable)(),
|
||||
__metadata("design:paramtypes", [core_1.Reflector,
|
||||
prisma_service_1.PrismaService,
|
||||
jwt_1.JwtService])
|
||||
], MaintenanceGuard);
|
||||
//# sourceMappingURL=maintenance.guard.js.map
|
||||
1
apps/api/dist/common/guards/maintenance.guard.js.map
vendored
Normal file
1
apps/api/dist/common/guards/maintenance.guard.js.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"maintenance.guard.js","sourceRoot":"","sources":["../../../src/common/guards/maintenance.guard.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAKwB;AACxB,uCAAyC;AACzC,gEAA4D;AAC5D,qCAAyC;AAGlC,IAAM,gBAAgB,GAAtB,MAAM,gBAAgB;IAEjB;IACA;IACA;IAHV,YACU,SAAoB,EACpB,MAAqB,EACrB,UAAsB;QAFtB,cAAS,GAAT,SAAS,CAAW;QACpB,WAAM,GAAN,MAAM,CAAe;QACrB,eAAU,GAAV,UAAU,CAAY;IAC7B,CAAC;IAEJ,KAAK,CAAC,WAAW,CAAC,OAAyB;QAEzC,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAU,iBAAiB,EAAE,OAAO,CAAC,UAAU,EAAE,CAAC,CAAC;QACtF,MAAM,kBAAkB,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAU,iBAAiB,EAAE,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC;QAE9F,IAAI,QAAQ,IAAI,kBAAkB,EAAE,CAAC;YACnC,OAAO,IAAI,CAAC;QACd,CAAC;QAGD,MAAM,iBAAiB,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,UAAU,CAAC;YAC/D,KAAK,EAAE,EAAE,GAAG,EAAE,kBAAkB,EAAE;SACnC,CAAC,CAAC;QAEH,MAAM,iBAAiB,GAAG,iBAAiB,EAAE,KAAK,KAAK,MAAM,CAAC;QAE9D,IAAI,CAAC,iBAAiB,EAAE,CAAC;YACvB,OAAO,IAAI,CAAC;QACd,CAAC;QAGD,MAAM,OAAO,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,UAAU,EAAE,CAAC;QACpD,MAAM,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC,aAAa,CAAC;QAEjD,IAAI,UAAU,IAAI,UAAU,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;YACnD,IAAI,CAAC;gBACH,MAAM,KAAK,GAAG,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;gBACtC,MAAM,OAAO,GAAG,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;gBAG9C,IAAI,OAAO,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;oBAC7B,OAAO,IAAI,CAAC;gBACd,CAAC;YACH,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;YAEjB,CAAC;QACH,CAAC;QAGD,MAAM,aAAa,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,UAAU,CAAC;YAC3D,KAAK,EAAE,EAAE,GAAG,EAAE,qBAAqB,EAAE;SACtC,CAAC,CAAC;QAEH,MAAM,OAAO,GACX,aAAa,EAAE,KAAK;YACpB,sDAAsD,CAAC;QAEzD,MAAM,IAAI,oCAA2B,CAAC;YACpC,UAAU,EAAE,GAAG;YACf,OAAO,EAAE,OAAO;YAChB,eAAe,EAAE,IAAI;SACtB,CAAC,CAAC;IACL,CAAC;CACF,CAAA;AA5DY,4CAAgB;2BAAhB,gBAAgB;IAD5B,IAAA,mBAAU,GAAE;qCAGU,gBAAS;QACZ,8BAAa;QACT,gBAAU;GAJrB,gBAAgB,CA4D5B"}
|
||||
2
apps/api/dist/health/health.controller.js
vendored
2
apps/api/dist/health/health.controller.js
vendored
@@ -12,6 +12,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.HealthController = void 0;
|
||||
const common_1 = require("@nestjs/common");
|
||||
const prisma_service_1 = require("../prisma/prisma.service");
|
||||
const skip_maintenance_decorator_1 = require("../common/decorators/skip-maintenance.decorator");
|
||||
let HealthController = class HealthController {
|
||||
prisma;
|
||||
constructor(prisma) {
|
||||
@@ -40,6 +41,7 @@ __decorate([
|
||||
], HealthController.prototype, "db", null);
|
||||
exports.HealthController = HealthController = __decorate([
|
||||
(0, common_1.Controller)('health'),
|
||||
(0, skip_maintenance_decorator_1.SkipMaintenance)(),
|
||||
__metadata("design:paramtypes", [prisma_service_1.PrismaService])
|
||||
], HealthController);
|
||||
//# sourceMappingURL=health.controller.js.map
|
||||
@@ -1 +1 @@
|
||||
{"version":3,"file":"health.controller.js","sourceRoot":"","sources":["../../src/health/health.controller.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAAiD;AACjD,6DAAyD;AAGlD,IAAM,gBAAgB,GAAtB,MAAM,gBAAgB;IACE;IAA7B,YAA6B,MAAqB;QAArB,WAAM,GAAN,MAAM,CAAe;IAAG,CAAC;IAGtD,EAAE;QACA,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;IAC1B,CAAC;IAGK,AAAN,KAAK,CAAC,EAAE;QAEN,MAAM,IAAI,CAAC,MAAM,CAAC,SAAS,CAAA,UAAU,CAAC;QACtC,OAAO,EAAE,EAAE,EAAE,WAAW,EAAE,CAAC;IAC7B,CAAC;CACF,CAAA;AAdY,4CAAgB;AAI3B;IADC,IAAA,YAAG,GAAE;;;;0CAGL;AAGK;IADL,IAAA,YAAG,EAAC,IAAI,CAAC;;;;0CAKT;2BAbU,gBAAgB;IAD5B,IAAA,mBAAU,EAAC,QAAQ,CAAC;qCAEkB,8BAAa;GADvC,gBAAgB,CAc5B"}
|
||||
{"version":3,"file":"health.controller.js","sourceRoot":"","sources":["../../src/health/health.controller.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAAiD;AACjD,6DAAyD;AACzD,gGAAkF;AAI3E,IAAM,gBAAgB,GAAtB,MAAM,gBAAgB;IACE;IAA7B,YAA6B,MAAqB;QAArB,WAAM,GAAN,MAAM,CAAe;IAAG,CAAC;IAGtD,EAAE;QACA,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;IAC1B,CAAC;IAGK,AAAN,KAAK,CAAC,EAAE;QAEN,MAAM,IAAI,CAAC,MAAM,CAAC,SAAS,CAAA,UAAU,CAAC;QACtC,OAAO,EAAE,EAAE,EAAE,WAAW,EAAE,CAAC;IAC7B,CAAC;CACF,CAAA;AAdY,4CAAgB;AAI3B;IADC,IAAA,YAAG,GAAE;;;;0CAGL;AAGK;IADL,IAAA,YAAG,EAAC,IAAI,CAAC;;;;0CAKT;2BAbU,gBAAgB;IAF5B,IAAA,mBAAU,EAAC,QAAQ,CAAC;IACpB,IAAA,4CAAe,GAAE;qCAEqB,8BAAa;GADvC,gBAAgB,CAc5B"}
|
||||
2
apps/api/dist/otp/otp.service.js.map
vendored
2
apps/api/dist/otp/otp.service.js.map
vendored
File diff suppressed because one or more lines are too long
54
apps/api/dist/seed.js
vendored
54
apps/api/dist/seed.js
vendored
@@ -107,10 +107,22 @@ async function main() {
|
||||
features: {
|
||||
wallets: { limit: null, label: 'Unlimited wallets' },
|
||||
goals: { limit: null, label: 'Unlimited goals' },
|
||||
team: { enabled: true, maxMembers: 10, label: 'Team feature (10 members)' },
|
||||
api: { enabled: true, rateLimit: 1000, label: 'API access (1000 req/hr)' },
|
||||
team: {
|
||||
enabled: true,
|
||||
maxMembers: 10,
|
||||
label: 'Team feature (10 members)',
|
||||
},
|
||||
api: {
|
||||
enabled: true,
|
||||
rateLimit: 1000,
|
||||
label: 'API access (1000 req/hr)',
|
||||
},
|
||||
support: { level: 'priority', label: 'Priority support' },
|
||||
export: { enabled: true, formats: ['csv', 'excel', 'pdf'], label: 'All export formats' },
|
||||
export: {
|
||||
enabled: true,
|
||||
formats: ['csv', 'excel', 'pdf'],
|
||||
label: 'All export formats',
|
||||
},
|
||||
},
|
||||
badge: 'Popular',
|
||||
badgeColor: 'blue',
|
||||
@@ -141,10 +153,22 @@ async function main() {
|
||||
features: {
|
||||
wallets: { limit: null, label: 'Unlimited wallets' },
|
||||
goals: { limit: null, label: 'Unlimited goals' },
|
||||
team: { enabled: true, maxMembers: 10, label: 'Team feature (10 members)' },
|
||||
api: { enabled: true, rateLimit: 1000, label: 'API access (1000 req/hr)' },
|
||||
team: {
|
||||
enabled: true,
|
||||
maxMembers: 10,
|
||||
label: 'Team feature (10 members)',
|
||||
},
|
||||
api: {
|
||||
enabled: true,
|
||||
rateLimit: 1000,
|
||||
label: 'API access (1000 req/hr)',
|
||||
},
|
||||
support: { level: 'priority', label: 'Priority support' },
|
||||
export: { enabled: true, formats: ['csv', 'excel', 'pdf'], label: 'All export formats' },
|
||||
export: {
|
||||
enabled: true,
|
||||
formats: ['csv', 'excel', 'pdf'],
|
||||
label: 'All export formats',
|
||||
},
|
||||
discount: { value: '17%', label: 'Save 17% (2 months free)' },
|
||||
},
|
||||
badge: 'Best Value',
|
||||
@@ -161,7 +185,11 @@ async function main() {
|
||||
apiRateLimit: 1000,
|
||||
},
|
||||
});
|
||||
console.log('✅ Plans created:', [freePlan.name, proMonthly.name, proYearly.name]);
|
||||
console.log('✅ Plans created:', [
|
||||
freePlan.name,
|
||||
proMonthly.name,
|
||||
proYearly.name,
|
||||
]);
|
||||
console.log('\n💳 Creating default payment methods...');
|
||||
const bcaMethod = await prisma.paymentMethod.upsert({
|
||||
where: { id: 'bca-method' },
|
||||
@@ -211,7 +239,11 @@ async function main() {
|
||||
sortOrder: 3,
|
||||
},
|
||||
});
|
||||
console.log('✅ Payment methods created:', [bcaMethod.displayName, mandiriMethod.displayName, gopayMethod.displayName]);
|
||||
console.log('✅ Payment methods created:', [
|
||||
bcaMethod.displayName,
|
||||
mandiriMethod.displayName,
|
||||
gopayMethod.displayName,
|
||||
]);
|
||||
console.log('\n⚙️ Creating app config...');
|
||||
await prisma.appConfig.upsert({
|
||||
where: { key: 'MAINTENANCE_MODE' },
|
||||
@@ -253,7 +285,11 @@ async function main() {
|
||||
console.log(' Admin Email:', ADMIN_EMAIL);
|
||||
console.log(' Admin Password:', ADMIN_PASSWORD);
|
||||
console.log(' Plans:', [freePlan.name, proMonthly.name, proYearly.name].join(', '));
|
||||
console.log(' Payment Methods:', [bcaMethod.displayName, mandiriMethod.displayName, gopayMethod.displayName].join(', '));
|
||||
console.log(' Payment Methods:', [
|
||||
bcaMethod.displayName,
|
||||
mandiriMethod.displayName,
|
||||
gopayMethod.displayName,
|
||||
].join(', '));
|
||||
console.log('\n⚠️ IMPORTANT: Change admin password after first login!');
|
||||
console.log('\n🔗 Login at: http://localhost:5174/auth/login');
|
||||
}
|
||||
|
||||
2
apps/api/dist/seed.js.map
vendored
2
apps/api/dist/seed.js.map
vendored
File diff suppressed because one or more lines are too long
2
apps/api/dist/tsconfig.build.tsbuildinfo
vendored
2
apps/api/dist/tsconfig.build.tsbuildinfo
vendored
File diff suppressed because one or more lines are too long
2
apps/api/dist/users/users.controller.js.map
vendored
2
apps/api/dist/users/users.controller.js.map
vendored
@@ -1 +1 @@
|
||||
{"version":3,"file":"users.controller.js","sourceRoot":"","sources":["../../src/users/users.controller.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA,2CAAoF;AACpF,mDAA+C;AAC/C,mDAA+C;AAWxC,IAAM,eAAe,GAArB,MAAM,eAAe;IACG;IAA7B,YAA6B,KAAmB;QAAnB,UAAK,GAAL,KAAK,CAAc;IAAG,CAAC;IAGpD,EAAE;QACA,OAAO,IAAI,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC;IACzB,CAAC;IAGK,AAAN,KAAK,CAAC,aAAa,CACV,GAAoB,EACnB,IAAuC;QAE/C,OAAO,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IACzD,CAAC;IAGK,AAAN,KAAK,CAAC,WAAW,CAAQ,GAAoB;QAC3C,OAAO,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACjD,CAAC;IAGK,AAAN,KAAK,CAAC,aAAa,CACV,GAAoB,EACnB,IAA0B;QAElC,OAAO,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;IAClE,CAAC;CACF,CAAA;AA5BY,0CAAe;AAI1B;IADC,IAAA,YAAG,EAAC,IAAI,CAAC;;;;yCAGT;AAGK;IADL,IAAA,YAAG,EAAC,SAAS,CAAC;IAEZ,WAAA,IAAA,YAAG,GAAE,CAAA;IACL,WAAA,IAAA,aAAI,GAAE,CAAA;;;;oDAGR;AAGK;IADL,IAAA,YAAG,EAAC,WAAW,CAAC;IACE,WAAA,IAAA,YAAG,GAAE,CAAA;;;;kDAEvB;AAGK;IADL,IAAA,eAAM,EAAC,SAAS,CAAC;IAEf,WAAA,IAAA,YAAG,GAAE,CAAA;IACL,WAAA,IAAA,aAAI,GAAE,CAAA;;;;oDAGR;0BA3BU,eAAe;IAF3B,IAAA,mBAAU,EAAC,OAAO,CAAC;IACnB,IAAA,kBAAS,EAAC,sBAAS,CAAC;qCAEiB,4BAAY;GADrC,eAAe,CA4B3B"}
|
||||
{"version":3,"file":"users.controller.js","sourceRoot":"","sources":["../../src/users/users.controller.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA,2CAQwB;AACxB,mDAA+C;AAC/C,mDAA+C;AAWxC,IAAM,eAAe,GAArB,MAAM,eAAe;IACG;IAA7B,YAA6B,KAAmB;QAAnB,UAAK,GAAL,KAAK,CAAc;IAAG,CAAC;IAGpD,EAAE;QACA,OAAO,IAAI,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC;IACzB,CAAC;IAGK,AAAN,KAAK,CAAC,aAAa,CACV,GAAoB,EACnB,IAAuC;QAE/C,OAAO,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IACzD,CAAC;IAGK,AAAN,KAAK,CAAC,WAAW,CAAQ,GAAoB;QAC3C,OAAO,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACjD,CAAC;IAGK,AAAN,KAAK,CAAC,aAAa,CACV,GAAoB,EACnB,IAA0B;QAElC,OAAO,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;IAClE,CAAC;CACF,CAAA;AA5BY,0CAAe;AAI1B;IADC,IAAA,YAAG,EAAC,IAAI,CAAC;;;;yCAGT;AAGK;IADL,IAAA,YAAG,EAAC,SAAS,CAAC;IAEZ,WAAA,IAAA,YAAG,GAAE,CAAA;IACL,WAAA,IAAA,aAAI,GAAE,CAAA;;;;oDAGR;AAGK;IADL,IAAA,YAAG,EAAC,WAAW,CAAC;IACE,WAAA,IAAA,YAAG,GAAE,CAAA;;;;kDAEvB;AAGK;IADL,IAAA,eAAM,EAAC,SAAS,CAAC;IAEf,WAAA,IAAA,YAAG,GAAE,CAAA;IACL,WAAA,IAAA,aAAI,GAAE,CAAA;;;;oDAGR;0BA3BU,eAAe;IAF3B,IAAA,mBAAU,EAAC,OAAO,CAAC;IACnB,IAAA,kBAAS,EAAC,sBAAS,CAAC;qCAEiB,4BAAY;GADrC,eAAe,CA4B3B"}
|
||||
2
apps/api/dist/users/users.service.js.map
vendored
2
apps/api/dist/users/users.service.js.map
vendored
@@ -1 +1 @@
|
||||
{"version":3,"file":"users.service.js","sourceRoot":"","sources":["../../src/users/users.service.ts"],"names":[],"mappings":"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"}
|
||||
{"version":3,"file":"users.service.js","sourceRoot":"","sources":["../../src/users/users.service.ts"],"names":[],"mappings":"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"}
|
||||
28
apps/api/dist/wallets/wallets.controller.d.ts
vendored
28
apps/api/dist/wallets/wallets.controller.d.ts
vendored
@@ -11,11 +11,11 @@ export declare class WalletsController {
|
||||
constructor(wallets: WalletsService, transactions: TransactionsService);
|
||||
list(req: RequestWithUser): import("@prisma/client").Prisma.PrismaPromise<{
|
||||
id: string;
|
||||
userId: string;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
kind: string;
|
||||
name: string;
|
||||
userId: string;
|
||||
kind: string;
|
||||
currency: string | null;
|
||||
unit: string | null;
|
||||
initialAmount: import("@prisma/client/runtime/library").Decimal | null;
|
||||
@@ -23,15 +23,15 @@ export declare class WalletsController {
|
||||
deletedAt: Date | null;
|
||||
}[]>;
|
||||
getAllTransactions(req: RequestWithUser): Promise<{
|
||||
category: string | null;
|
||||
id: string;
|
||||
userId: string;
|
||||
createdAt: Date;
|
||||
walletId: string;
|
||||
date: Date;
|
||||
userId: string;
|
||||
amount: import("@prisma/client/runtime/library").Decimal;
|
||||
direction: string;
|
||||
category: string | null;
|
||||
date: Date;
|
||||
memo: string | null;
|
||||
walletId: string;
|
||||
recurrenceId: string | null;
|
||||
}[]>;
|
||||
create(req: RequestWithUser, body: {
|
||||
@@ -43,11 +43,11 @@ export declare class WalletsController {
|
||||
pricePerUnit?: number;
|
||||
}): import("@prisma/client").Prisma.Prisma__WalletClient<{
|
||||
id: string;
|
||||
userId: string;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
kind: string;
|
||||
name: string;
|
||||
userId: string;
|
||||
kind: string;
|
||||
currency: string | null;
|
||||
unit: string | null;
|
||||
initialAmount: import("@prisma/client/runtime/library").Decimal | null;
|
||||
@@ -65,11 +65,11 @@ export declare class WalletsController {
|
||||
pricePerUnit?: number;
|
||||
}): import("@prisma/client").Prisma.Prisma__WalletClient<{
|
||||
id: string;
|
||||
userId: string;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
kind: string;
|
||||
name: string;
|
||||
userId: string;
|
||||
kind: string;
|
||||
currency: string | null;
|
||||
unit: string | null;
|
||||
initialAmount: import("@prisma/client/runtime/library").Decimal | null;
|
||||
@@ -86,11 +86,11 @@ export declare class WalletsController {
|
||||
updated: number;
|
||||
wallets: {
|
||||
id: string;
|
||||
userId: string;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
kind: string;
|
||||
name: string;
|
||||
userId: string;
|
||||
kind: string;
|
||||
currency: string | null;
|
||||
unit: string | null;
|
||||
initialAmount: import("@prisma/client/runtime/library").Decimal | null;
|
||||
@@ -102,11 +102,11 @@ export declare class WalletsController {
|
||||
};
|
||||
delete(req: RequestWithUser, id: string): import("@prisma/client").Prisma.Prisma__WalletClient<{
|
||||
id: string;
|
||||
userId: string;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
kind: string;
|
||||
name: string;
|
||||
userId: string;
|
||||
kind: string;
|
||||
currency: string | null;
|
||||
unit: string | null;
|
||||
initialAmount: import("@prisma/client/runtime/library").Decimal | null;
|
||||
|
||||
20
apps/api/dist/wallets/wallets.service.d.ts
vendored
20
apps/api/dist/wallets/wallets.service.d.ts
vendored
@@ -4,11 +4,11 @@ export declare class WalletsService {
|
||||
constructor(prisma: PrismaService);
|
||||
list(userId: string): import("@prisma/client").Prisma.PrismaPromise<{
|
||||
id: string;
|
||||
userId: string;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
kind: string;
|
||||
name: string;
|
||||
userId: string;
|
||||
kind: string;
|
||||
currency: string | null;
|
||||
unit: string | null;
|
||||
initialAmount: import("@prisma/client/runtime/library").Decimal | null;
|
||||
@@ -24,11 +24,11 @@ export declare class WalletsService {
|
||||
pricePerUnit?: number;
|
||||
}): import("@prisma/client").Prisma.Prisma__WalletClient<{
|
||||
id: string;
|
||||
userId: string;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
kind: string;
|
||||
name: string;
|
||||
userId: string;
|
||||
kind: string;
|
||||
currency: string | null;
|
||||
unit: string | null;
|
||||
initialAmount: import("@prisma/client/runtime/library").Decimal | null;
|
||||
@@ -44,11 +44,11 @@ export declare class WalletsService {
|
||||
pricePerUnit?: number;
|
||||
}): import("@prisma/client").Prisma.Prisma__WalletClient<{
|
||||
id: string;
|
||||
userId: string;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
kind: string;
|
||||
name: string;
|
||||
userId: string;
|
||||
kind: string;
|
||||
currency: string | null;
|
||||
unit: string | null;
|
||||
initialAmount: import("@prisma/client/runtime/library").Decimal | null;
|
||||
@@ -63,11 +63,11 @@ export declare class WalletsService {
|
||||
updated: number;
|
||||
wallets: {
|
||||
id: string;
|
||||
userId: string;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
kind: string;
|
||||
name: string;
|
||||
userId: string;
|
||||
kind: string;
|
||||
currency: string | null;
|
||||
unit: string | null;
|
||||
initialAmount: import("@prisma/client/runtime/library").Decimal | null;
|
||||
@@ -77,11 +77,11 @@ export declare class WalletsService {
|
||||
}>;
|
||||
delete(userId: string, id: string): import("@prisma/client").Prisma.Prisma__WalletClient<{
|
||||
id: string;
|
||||
userId: string;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
kind: string;
|
||||
name: string;
|
||||
userId: string;
|
||||
kind: string;
|
||||
currency: string | null;
|
||||
unit: string | null;
|
||||
initialAmount: import("@prisma/client/runtime/library").Decimal | null;
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
import { AuthGuard } from '../auth/auth.guard';
|
||||
import { AdminGuard } from './guards/admin.guard';
|
||||
import { AdminConfigService } from './admin-config.service';
|
||||
import { SkipMaintenance } from '../common/decorators/skip-maintenance.decorator';
|
||||
|
||||
interface RequestWithUser {
|
||||
user: {
|
||||
@@ -21,6 +22,7 @@ interface RequestWithUser {
|
||||
|
||||
@Controller('admin/config')
|
||||
@UseGuards(AuthGuard, AdminGuard)
|
||||
@SkipMaintenance()
|
||||
export class AdminConfigController {
|
||||
constructor(private readonly service: AdminConfigService) {}
|
||||
|
||||
|
||||
@@ -42,15 +42,18 @@ export class AdminConfigService {
|
||||
|
||||
async getByCategory() {
|
||||
const configs = await this.prisma.appConfig.findMany();
|
||||
|
||||
|
||||
// Group by category
|
||||
const grouped = configs.reduce((acc, config) => {
|
||||
if (!acc[config.category]) {
|
||||
acc[config.category] = [];
|
||||
}
|
||||
acc[config.category].push(config);
|
||||
return acc;
|
||||
}, {} as Record<string, any[]>);
|
||||
const grouped = configs.reduce(
|
||||
(acc, config) => {
|
||||
if (!acc[config.category]) {
|
||||
acc[config.category] = [];
|
||||
}
|
||||
acc[config.category].push(config);
|
||||
return acc;
|
||||
},
|
||||
{} as Record<string, any[]>,
|
||||
);
|
||||
|
||||
return grouped;
|
||||
}
|
||||
|
||||
@@ -11,9 +11,11 @@ import {
|
||||
import { AuthGuard } from '../auth/auth.guard';
|
||||
import { AdminGuard } from './guards/admin.guard';
|
||||
import { AdminPaymentMethodsService } from './admin-payment-methods.service';
|
||||
import { SkipMaintenance } from '../common/decorators/skip-maintenance.decorator';
|
||||
|
||||
@Controller('admin/payment-methods')
|
||||
@UseGuards(AuthGuard, AdminGuard)
|
||||
@SkipMaintenance()
|
||||
export class AdminPaymentMethodsController {
|
||||
constructor(private readonly service: AdminPaymentMethodsService) {}
|
||||
|
||||
|
||||
@@ -41,9 +41,9 @@ export class AdminPaymentMethodsService {
|
||||
this.prisma.paymentMethod.update({
|
||||
where: { id },
|
||||
data: { sortOrder: index + 1 },
|
||||
})
|
||||
}),
|
||||
);
|
||||
|
||||
|
||||
await this.prisma.$transaction(updates);
|
||||
return { success: true };
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
import { AuthGuard } from '../auth/auth.guard';
|
||||
import { AdminGuard } from './guards/admin.guard';
|
||||
import { AdminPaymentsService } from './admin-payments.service';
|
||||
import { SkipMaintenance } from '../common/decorators/skip-maintenance.decorator';
|
||||
|
||||
interface RequestWithUser {
|
||||
user: {
|
||||
@@ -20,6 +21,7 @@ interface RequestWithUser {
|
||||
|
||||
@Controller('admin/payments')
|
||||
@UseGuards(AuthGuard, AdminGuard)
|
||||
@SkipMaintenance()
|
||||
export class AdminPaymentsController {
|
||||
constructor(private readonly service: AdminPaymentsService) {}
|
||||
|
||||
|
||||
@@ -72,7 +72,7 @@ export class AdminPaymentsService {
|
||||
const plan = payment.subscription.plan;
|
||||
const now = new Date();
|
||||
const endDate = new Date(now);
|
||||
|
||||
|
||||
if (plan.durationDays) {
|
||||
endDate.setDate(endDate.getDate() + plan.durationDays);
|
||||
}
|
||||
@@ -82,7 +82,8 @@ export class AdminPaymentsService {
|
||||
data: {
|
||||
status: 'active',
|
||||
startDate: now,
|
||||
endDate: plan.durationType === 'lifetime' ? new Date('2099-12-31') : endDate,
|
||||
endDate:
|
||||
plan.durationType === 'lifetime' ? new Date('2099-12-31') : endDate,
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -127,18 +128,32 @@ export class AdminPaymentsService {
|
||||
});
|
||||
|
||||
// Group by month
|
||||
const monthlyData: { [key: string]: { revenue: number; count: number } } = {};
|
||||
const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
|
||||
const monthlyData: { [key: string]: { revenue: number; count: number } } =
|
||||
{};
|
||||
const months = [
|
||||
'Jan',
|
||||
'Feb',
|
||||
'Mar',
|
||||
'Apr',
|
||||
'May',
|
||||
'Jun',
|
||||
'Jul',
|
||||
'Aug',
|
||||
'Sep',
|
||||
'Oct',
|
||||
'Nov',
|
||||
'Dec',
|
||||
];
|
||||
|
||||
payments.forEach((payment) => {
|
||||
if (payment.paidAt) {
|
||||
const date = new Date(payment.paidAt);
|
||||
const monthKey = `${months[date.getMonth()]} ${date.getFullYear()}`;
|
||||
|
||||
|
||||
if (!monthlyData[monthKey]) {
|
||||
monthlyData[monthKey] = { revenue: 0, count: 0 };
|
||||
}
|
||||
|
||||
|
||||
monthlyData[monthKey].revenue += Number(payment.amount);
|
||||
monthlyData[monthKey].count += 1;
|
||||
}
|
||||
|
||||
@@ -11,9 +11,11 @@ import {
|
||||
import { AuthGuard } from '../auth/auth.guard';
|
||||
import { AdminGuard } from './guards/admin.guard';
|
||||
import { AdminPlansService } from './admin-plans.service';
|
||||
import { SkipMaintenance } from '../common/decorators/skip-maintenance.decorator';
|
||||
|
||||
@Controller('admin/plans')
|
||||
@UseGuards(AuthGuard, AdminGuard)
|
||||
@SkipMaintenance()
|
||||
export class AdminPlansController {
|
||||
constructor(private readonly plansService: AdminPlansService) {}
|
||||
|
||||
|
||||
@@ -86,9 +86,9 @@ export class AdminPlansService {
|
||||
this.prisma.plan.update({
|
||||
where: { id },
|
||||
data: { sortOrder: index + 1 },
|
||||
})
|
||||
}),
|
||||
);
|
||||
|
||||
|
||||
await this.prisma.$transaction(updates);
|
||||
return { success: true };
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ import {
|
||||
Get,
|
||||
Post,
|
||||
Put,
|
||||
Delete,
|
||||
Body,
|
||||
Param,
|
||||
Query,
|
||||
@@ -11,9 +12,11 @@ import {
|
||||
import { AuthGuard } from '../auth/auth.guard';
|
||||
import { AdminGuard } from './guards/admin.guard';
|
||||
import { AdminUsersService } from './admin-users.service';
|
||||
import { SkipMaintenance } from '../common/decorators/skip-maintenance.decorator';
|
||||
|
||||
@Controller('admin/users')
|
||||
@UseGuards(AuthGuard, AdminGuard)
|
||||
@SkipMaintenance()
|
||||
export class AdminUsersController {
|
||||
constructor(private readonly service: AdminUsersService) {}
|
||||
|
||||
@@ -54,4 +57,30 @@ export class AdminUsersController {
|
||||
) {
|
||||
return this.service.grantProAccess(id, body.planSlug, body.durationDays);
|
||||
}
|
||||
|
||||
@Post()
|
||||
create(
|
||||
@Body()
|
||||
body: {
|
||||
email: string;
|
||||
password: string;
|
||||
name?: string;
|
||||
role?: string;
|
||||
},
|
||||
) {
|
||||
return this.service.create(body);
|
||||
}
|
||||
|
||||
@Put(':id')
|
||||
update(
|
||||
@Param('id') id: string,
|
||||
@Body() body: { email?: string; name?: string; role?: string },
|
||||
) {
|
||||
return this.service.update(id, body);
|
||||
}
|
||||
|
||||
@Delete(':id')
|
||||
delete(@Param('id') id: string) {
|
||||
return this.service.delete(id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import {
|
||||
Injectable,
|
||||
ConflictException,
|
||||
NotFoundException,
|
||||
} from '@nestjs/common';
|
||||
import { PrismaService } from '../prisma/prisma.service';
|
||||
import * as bcrypt from 'bcrypt';
|
||||
|
||||
@Injectable()
|
||||
export class AdminUsersService {
|
||||
@@ -140,4 +145,102 @@ export class AdminUsersService {
|
||||
suspendedUsers,
|
||||
};
|
||||
}
|
||||
|
||||
async create(data: {
|
||||
email: string;
|
||||
password: string;
|
||||
name?: string;
|
||||
role?: string;
|
||||
}) {
|
||||
// Check if user already exists
|
||||
const existing = await this.prisma.user.findUnique({
|
||||
where: { email: data.email },
|
||||
});
|
||||
|
||||
if (existing) {
|
||||
throw new ConflictException('User with this email already exists');
|
||||
}
|
||||
|
||||
// Hash password
|
||||
const hashedPassword = await bcrypt.hash(data.password, 10);
|
||||
|
||||
// Create user
|
||||
return this.prisma.user.create({
|
||||
data: {
|
||||
email: data.email,
|
||||
passwordHash: hashedPassword,
|
||||
name: data.name || null,
|
||||
role: data.role || 'user',
|
||||
emailVerified: true, // Admin-created users are auto-verified
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
email: true,
|
||||
name: true,
|
||||
role: true,
|
||||
emailVerified: true,
|
||||
createdAt: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async update(
|
||||
id: string,
|
||||
data: { email?: string; name?: string; role?: string },
|
||||
) {
|
||||
// Check if user exists
|
||||
const user = await this.prisma.user.findUnique({
|
||||
where: { id },
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
throw new NotFoundException('User not found');
|
||||
}
|
||||
|
||||
// If email is being updated, check if it's already taken
|
||||
if (data.email && data.email !== user.email) {
|
||||
const existing = await this.prisma.user.findUnique({
|
||||
where: { email: data.email },
|
||||
});
|
||||
|
||||
if (existing) {
|
||||
throw new ConflictException('Email already in use');
|
||||
}
|
||||
}
|
||||
|
||||
return this.prisma.user.update({
|
||||
where: { id },
|
||||
data: {
|
||||
...(data.email && { email: data.email }),
|
||||
...(data.name !== undefined && { name: data.name }),
|
||||
...(data.role && { role: data.role }),
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
email: true,
|
||||
name: true,
|
||||
role: true,
|
||||
emailVerified: true,
|
||||
createdAt: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async delete(id: string) {
|
||||
// Check if user exists
|
||||
const user = await this.prisma.user.findUnique({
|
||||
where: { id },
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
throw new NotFoundException('User not found');
|
||||
}
|
||||
|
||||
// Delete user (cascade will handle related records)
|
||||
await this.prisma.user.delete({
|
||||
where: { id },
|
||||
});
|
||||
|
||||
return { message: 'User deleted successfully' };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { ConfigModule } from '@nestjs/config';
|
||||
import { APP_GUARD } from '@nestjs/core';
|
||||
import * as path from 'path';
|
||||
import { PrismaModule } from './prisma/prisma.module';
|
||||
import { AuthModule } from './auth/auth.module';
|
||||
@@ -10,6 +11,7 @@ import { TransactionsModule } from './transactions/transactions.module';
|
||||
import { CategoriesModule } from './categories/categories.module';
|
||||
import { OtpModule } from './otp/otp.module';
|
||||
import { AdminModule } from './admin/admin.module';
|
||||
import { MaintenanceGuard } from './common/guards/maintenance.guard';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
@@ -30,6 +32,11 @@ import { AdminModule } from './admin/admin.module';
|
||||
AdminModule,
|
||||
],
|
||||
controllers: [HealthController],
|
||||
providers: [],
|
||||
providers: [
|
||||
{
|
||||
provide: APP_GUARD,
|
||||
useClass: MaintenanceGuard,
|
||||
},
|
||||
],
|
||||
})
|
||||
export class AppModule {}
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
import { AuthGuard as JwtAuthGuard } from './auth.guard';
|
||||
import { AuthGuard } from '@nestjs/passport';
|
||||
import { AuthService } from './auth.service';
|
||||
import { SkipMaintenance } from '../common/decorators/skip-maintenance.decorator';
|
||||
import type { Response } from 'express';
|
||||
|
||||
interface RequestWithUser {
|
||||
@@ -24,6 +25,7 @@ export class AuthController {
|
||||
constructor(private authService: AuthService) {}
|
||||
|
||||
@Post('register')
|
||||
@SkipMaintenance()
|
||||
async register(
|
||||
@Body() body: { email: string; password: string; name?: string },
|
||||
) {
|
||||
@@ -31,11 +33,13 @@ export class AuthController {
|
||||
}
|
||||
|
||||
@Post('login')
|
||||
@SkipMaintenance()
|
||||
async login(@Body() body: { email: string; password: string }) {
|
||||
return this.authService.login(body.email, body.password);
|
||||
}
|
||||
|
||||
@Post('verify-otp')
|
||||
@SkipMaintenance()
|
||||
async verifyOtp(
|
||||
@Body()
|
||||
body: {
|
||||
@@ -52,12 +56,14 @@ export class AuthController {
|
||||
}
|
||||
|
||||
@Get('google')
|
||||
@SkipMaintenance()
|
||||
@UseGuards(AuthGuard('google'))
|
||||
async googleAuth() {
|
||||
// Initiates Google OAuth flow
|
||||
}
|
||||
|
||||
@Get('google/callback')
|
||||
@SkipMaintenance()
|
||||
@UseGuards(AuthGuard('google'))
|
||||
async googleAuthCallback(@Req() req: any, @Res() res: Response) {
|
||||
// Handle Google OAuth callback
|
||||
|
||||
@@ -20,6 +20,6 @@ import { OtpModule } from '../otp/otp.module';
|
||||
],
|
||||
controllers: [AuthController],
|
||||
providers: [AuthService, JwtStrategy, GoogleStrategy],
|
||||
exports: [AuthService],
|
||||
exports: [AuthService, JwtModule],
|
||||
})
|
||||
export class AuthModule {}
|
||||
|
||||
@@ -370,7 +370,7 @@ export class AuthService {
|
||||
where: { id: userId },
|
||||
select: { role: true },
|
||||
});
|
||||
|
||||
|
||||
return this.jwtService.sign({
|
||||
sub: userId,
|
||||
email,
|
||||
|
||||
@@ -21,10 +21,10 @@ export class JwtStrategy extends PassportStrategy(Strategy) {
|
||||
}
|
||||
|
||||
async validate(payload: JwtPayload) {
|
||||
return {
|
||||
userId: payload.sub,
|
||||
return {
|
||||
userId: payload.sub,
|
||||
email: payload.email,
|
||||
role: payload.role || 'user'
|
||||
role: payload.role || 'user',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,13 @@
|
||||
import { Controller, Get, Post, Body, Param, Delete, Req, UseGuards } from '@nestjs/common';
|
||||
import {
|
||||
Controller,
|
||||
Get,
|
||||
Post,
|
||||
Body,
|
||||
Param,
|
||||
Delete,
|
||||
Req,
|
||||
UseGuards,
|
||||
} from '@nestjs/common';
|
||||
import { CategoriesService } from '../categories/categories.service';
|
||||
import { CreateCategoryDto } from '../categories/dto/create-category.dto';
|
||||
import { AuthGuard } from '../auth/auth.guard';
|
||||
@@ -15,7 +24,10 @@ export class CategoriesController {
|
||||
constructor(private readonly categoriesService: CategoriesService) {}
|
||||
|
||||
@Post()
|
||||
create(@Req() req: RequestWithUser, @Body() createCategoryDto: CreateCategoryDto) {
|
||||
create(
|
||||
@Req() req: RequestWithUser,
|
||||
@Body() createCategoryDto: CreateCategoryDto,
|
||||
) {
|
||||
return this.categoriesService.create({
|
||||
...createCategoryDto,
|
||||
userId: req.user.userId,
|
||||
@@ -31,4 +43,4 @@ export class CategoriesController {
|
||||
remove(@Req() req: RequestWithUser, @Param('id') id: string) {
|
||||
return this.categoriesService.remove(id, req.user.userId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
import { SetMetadata } from '@nestjs/common';
|
||||
|
||||
export const SkipMaintenance = () => SetMetadata('skipMaintenance', true);
|
||||
72
apps/api/src/common/guards/maintenance.guard.ts
Normal file
72
apps/api/src/common/guards/maintenance.guard.ts
Normal file
@@ -0,0 +1,72 @@
|
||||
import {
|
||||
Injectable,
|
||||
CanActivate,
|
||||
ExecutionContext,
|
||||
ServiceUnavailableException,
|
||||
} from '@nestjs/common';
|
||||
import { Reflector } from '@nestjs/core';
|
||||
import { PrismaService } from '../../prisma/prisma.service';
|
||||
import { JwtService } from '@nestjs/jwt';
|
||||
|
||||
@Injectable()
|
||||
export class MaintenanceGuard implements CanActivate {
|
||||
constructor(
|
||||
private reflector: Reflector,
|
||||
private prisma: PrismaService,
|
||||
private jwtService: JwtService,
|
||||
) {}
|
||||
|
||||
async canActivate(context: ExecutionContext): Promise<boolean> {
|
||||
// Check if route is exempt from maintenance mode (auth, health, admin routes)
|
||||
const isExempt = this.reflector.get<boolean>('skipMaintenance', context.getHandler());
|
||||
const isControllerExempt = this.reflector.get<boolean>('skipMaintenance', context.getClass());
|
||||
|
||||
if (isExempt || isControllerExempt) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Get maintenance mode status from config
|
||||
const maintenanceConfig = await this.prisma.appConfig.findUnique({
|
||||
where: { key: 'maintenance_mode' },
|
||||
});
|
||||
|
||||
const isMaintenanceMode = maintenanceConfig?.value === 'true';
|
||||
|
||||
if (!isMaintenanceMode) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Try to extract user from JWT token (if exists)
|
||||
const request = context.switchToHttp().getRequest();
|
||||
const authHeader = request.headers.authorization;
|
||||
|
||||
if (authHeader && authHeader.startsWith('Bearer ')) {
|
||||
try {
|
||||
const token = authHeader.substring(7);
|
||||
const payload = this.jwtService.verify(token);
|
||||
|
||||
// If user is admin, allow access
|
||||
if (payload.role === 'admin') {
|
||||
return true;
|
||||
}
|
||||
} catch (error) {
|
||||
// Invalid token, continue to block
|
||||
}
|
||||
}
|
||||
|
||||
// Get maintenance message
|
||||
const messageConfig = await this.prisma.appConfig.findUnique({
|
||||
where: { key: 'maintenance_message' },
|
||||
});
|
||||
|
||||
const message =
|
||||
messageConfig?.value ||
|
||||
'System is under maintenance. Please try again later.';
|
||||
|
||||
throw new ServiceUnavailableException({
|
||||
statusCode: 503,
|
||||
message: message,
|
||||
maintenanceMode: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,9 @@
|
||||
import { Controller, Get } from '@nestjs/common';
|
||||
import { PrismaService } from '../prisma/prisma.service';
|
||||
import { SkipMaintenance } from '../common/decorators/skip-maintenance.decorator';
|
||||
|
||||
@Controller('health')
|
||||
@SkipMaintenance()
|
||||
export class HealthController {
|
||||
constructor(private readonly prisma: PrismaService) {}
|
||||
|
||||
|
||||
@@ -404,7 +404,9 @@ export class OtpService {
|
||||
} catch (error: unknown) {
|
||||
console.error('Failed to check WhatsApp number:', error);
|
||||
// Return false if webhook fails - safer approach
|
||||
console.log(`📱 Failed to check WhatsApp number: ${phone} - Webhook error`);
|
||||
console.log(
|
||||
`📱 Failed to check WhatsApp number: ${phone} - Webhook error`,
|
||||
);
|
||||
return {
|
||||
success: false,
|
||||
isRegistered: false,
|
||||
|
||||
@@ -16,9 +16,9 @@ async function main() {
|
||||
// 1. CREATE ADMIN USER
|
||||
// ============================================
|
||||
console.log('\n👤 Creating admin user...');
|
||||
|
||||
|
||||
const passwordHash = await bcrypt.hash(ADMIN_PASSWORD, 10);
|
||||
|
||||
|
||||
const admin = await prisma.user.upsert({
|
||||
where: { email: ADMIN_EMAIL },
|
||||
update: {
|
||||
@@ -34,14 +34,14 @@ async function main() {
|
||||
emailVerified: true,
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
console.log('✅ Admin user created:', admin.email);
|
||||
|
||||
// ============================================
|
||||
// 2. CREATE DEFAULT PLANS
|
||||
// ============================================
|
||||
console.log('\n💰 Creating default plans...');
|
||||
|
||||
|
||||
const freePlan = await prisma.plan.upsert({
|
||||
where: { slug: 'free' },
|
||||
update: {},
|
||||
@@ -74,7 +74,7 @@ async function main() {
|
||||
apiRateLimit: null,
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
const proMonthly = await prisma.plan.upsert({
|
||||
where: { slug: 'pro-monthly' },
|
||||
update: {},
|
||||
@@ -90,10 +90,22 @@ async function main() {
|
||||
features: {
|
||||
wallets: { limit: null, label: 'Unlimited wallets' },
|
||||
goals: { limit: null, label: 'Unlimited goals' },
|
||||
team: { enabled: true, maxMembers: 10, label: 'Team feature (10 members)' },
|
||||
api: { enabled: true, rateLimit: 1000, label: 'API access (1000 req/hr)' },
|
||||
team: {
|
||||
enabled: true,
|
||||
maxMembers: 10,
|
||||
label: 'Team feature (10 members)',
|
||||
},
|
||||
api: {
|
||||
enabled: true,
|
||||
rateLimit: 1000,
|
||||
label: 'API access (1000 req/hr)',
|
||||
},
|
||||
support: { level: 'priority', label: 'Priority support' },
|
||||
export: { enabled: true, formats: ['csv', 'excel', 'pdf'], label: 'All export formats' },
|
||||
export: {
|
||||
enabled: true,
|
||||
formats: ['csv', 'excel', 'pdf'],
|
||||
label: 'All export formats',
|
||||
},
|
||||
},
|
||||
badge: 'Popular',
|
||||
badgeColor: 'blue',
|
||||
@@ -109,7 +121,7 @@ async function main() {
|
||||
apiRateLimit: 1000,
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
const proYearly = await prisma.plan.upsert({
|
||||
where: { slug: 'pro-yearly' },
|
||||
update: {},
|
||||
@@ -125,10 +137,22 @@ async function main() {
|
||||
features: {
|
||||
wallets: { limit: null, label: 'Unlimited wallets' },
|
||||
goals: { limit: null, label: 'Unlimited goals' },
|
||||
team: { enabled: true, maxMembers: 10, label: 'Team feature (10 members)' },
|
||||
api: { enabled: true, rateLimit: 1000, label: 'API access (1000 req/hr)' },
|
||||
team: {
|
||||
enabled: true,
|
||||
maxMembers: 10,
|
||||
label: 'Team feature (10 members)',
|
||||
},
|
||||
api: {
|
||||
enabled: true,
|
||||
rateLimit: 1000,
|
||||
label: 'API access (1000 req/hr)',
|
||||
},
|
||||
support: { level: 'priority', label: 'Priority support' },
|
||||
export: { enabled: true, formats: ['csv', 'excel', 'pdf'], label: 'All export formats' },
|
||||
export: {
|
||||
enabled: true,
|
||||
formats: ['csv', 'excel', 'pdf'],
|
||||
label: 'All export formats',
|
||||
},
|
||||
discount: { value: '17%', label: 'Save 17% (2 months free)' },
|
||||
},
|
||||
badge: 'Best Value',
|
||||
@@ -145,14 +169,18 @@ async function main() {
|
||||
apiRateLimit: 1000,
|
||||
},
|
||||
});
|
||||
|
||||
console.log('✅ Plans created:', [freePlan.name, proMonthly.name, proYearly.name]);
|
||||
|
||||
console.log('✅ Plans created:', [
|
||||
freePlan.name,
|
||||
proMonthly.name,
|
||||
proYearly.name,
|
||||
]);
|
||||
|
||||
// ============================================
|
||||
// 3. CREATE DEFAULT PAYMENT METHODS
|
||||
// ============================================
|
||||
console.log('\n💳 Creating default payment methods...');
|
||||
|
||||
|
||||
const bcaMethod = await prisma.paymentMethod.upsert({
|
||||
where: { id: 'bca-method' },
|
||||
update: {},
|
||||
@@ -164,12 +192,13 @@ async function main() {
|
||||
accountNumber: '1234567890',
|
||||
displayName: 'BCA Virtual Account',
|
||||
logoUrl: '/logos/bca.png',
|
||||
instructions: 'Transfer to the account above and upload proof of payment.',
|
||||
instructions:
|
||||
'Transfer to the account above and upload proof of payment.',
|
||||
isActive: true,
|
||||
sortOrder: 1,
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
const mandiriMethod = await prisma.paymentMethod.upsert({
|
||||
where: { id: 'mandiri-method' },
|
||||
update: {},
|
||||
@@ -181,12 +210,13 @@ async function main() {
|
||||
accountNumber: '9876543210',
|
||||
displayName: 'Mandiri Virtual Account',
|
||||
logoUrl: '/logos/mandiri.png',
|
||||
instructions: 'Transfer to the account above and upload proof of payment.',
|
||||
instructions:
|
||||
'Transfer to the account above and upload proof of payment.',
|
||||
isActive: true,
|
||||
sortOrder: 2,
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
const gopayMethod = await prisma.paymentMethod.upsert({
|
||||
where: { id: 'gopay-method' },
|
||||
update: {},
|
||||
@@ -203,14 +233,18 @@ async function main() {
|
||||
sortOrder: 3,
|
||||
},
|
||||
});
|
||||
|
||||
console.log('✅ Payment methods created:', [bcaMethod.displayName, mandiriMethod.displayName, gopayMethod.displayName]);
|
||||
|
||||
console.log('✅ Payment methods created:', [
|
||||
bcaMethod.displayName,
|
||||
mandiriMethod.displayName,
|
||||
gopayMethod.displayName,
|
||||
]);
|
||||
|
||||
// ============================================
|
||||
// 4. CREATE APP CONFIG (Optional)
|
||||
// ============================================
|
||||
console.log('\n⚙️ Creating app config...');
|
||||
|
||||
|
||||
await prisma.appConfig.upsert({
|
||||
where: { key: 'MAINTENANCE_MODE' },
|
||||
update: {},
|
||||
@@ -224,14 +258,14 @@ async function main() {
|
||||
isSecret: false,
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
console.log('✅ App config created');
|
||||
|
||||
// ============================================
|
||||
// 5. CREATE TEMP USER & WALLET (Legacy)
|
||||
// ============================================
|
||||
console.log('\n🔧 Creating temp user (legacy)...');
|
||||
|
||||
|
||||
const user = await prisma.user.upsert({
|
||||
where: { id: TEMP_USER_ID },
|
||||
update: {},
|
||||
@@ -252,7 +286,7 @@ async function main() {
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
console.log('✅ Temp user created:', user.id);
|
||||
|
||||
// ============================================
|
||||
@@ -262,8 +296,18 @@ async function main() {
|
||||
console.log('\n📋 Summary:');
|
||||
console.log(' Admin Email:', ADMIN_EMAIL);
|
||||
console.log(' Admin Password:', ADMIN_PASSWORD);
|
||||
console.log(' Plans:', [freePlan.name, proMonthly.name, proYearly.name].join(', '));
|
||||
console.log(' Payment Methods:', [bcaMethod.displayName, mandiriMethod.displayName, gopayMethod.displayName].join(', '));
|
||||
console.log(
|
||||
' Plans:',
|
||||
[freePlan.name, proMonthly.name, proYearly.name].join(', '),
|
||||
);
|
||||
console.log(
|
||||
' Payment Methods:',
|
||||
[
|
||||
bcaMethod.displayName,
|
||||
mandiriMethod.displayName,
|
||||
gopayMethod.displayName,
|
||||
].join(', '),
|
||||
);
|
||||
console.log('\n⚠️ IMPORTANT: Change admin password after first login!');
|
||||
console.log('\n🔗 Login at: http://localhost:5174/auth/login');
|
||||
}
|
||||
|
||||
@@ -1,4 +1,12 @@
|
||||
import { Controller, Get, Put, Delete, Body, Req, UseGuards } from '@nestjs/common';
|
||||
import {
|
||||
Controller,
|
||||
Get,
|
||||
Put,
|
||||
Delete,
|
||||
Body,
|
||||
Req,
|
||||
UseGuards,
|
||||
} from '@nestjs/common';
|
||||
import { AuthGuard } from '../auth/auth.guard';
|
||||
import { UsersService } from './users.service';
|
||||
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
import { Injectable, BadRequestException, UnauthorizedException } from '@nestjs/common';
|
||||
import {
|
||||
Injectable,
|
||||
BadRequestException,
|
||||
UnauthorizedException,
|
||||
} from '@nestjs/common';
|
||||
import { PrismaService } from '../prisma/prisma.service';
|
||||
import { getTempUserId } from '../common/user.util';
|
||||
import * as bcrypt from 'bcrypt';
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { BrowserRouter, Routes, Route, Navigate } from 'react-router-dom'
|
||||
import { useState, useEffect } from 'react'
|
||||
import { AuthProvider, useAuth } from './contexts/AuthContext'
|
||||
import { LanguageProvider } from './contexts/LanguageContext'
|
||||
import { ThemeProvider } from './components/ThemeProvider'
|
||||
@@ -8,14 +9,16 @@ import { Login } from './components/pages/Login'
|
||||
import { Register } from './components/pages/Register'
|
||||
import { OtpVerification } from './components/pages/OtpVerification'
|
||||
import { AuthCallback } from './components/pages/AuthCallback'
|
||||
import { MaintenancePage } from './components/pages/MaintenancePage'
|
||||
import { AdminLayout } from './components/admin/AdminLayout'
|
||||
import { AdminDashboard } from './components/admin/pages/AdminDashboard'
|
||||
import { AdminPlans } from './components/admin/pages/AdminPlans'
|
||||
import { AdminPaymentMethods } from './components/admin/pages/AdminPaymentMethods'
|
||||
import { AdminPayments } from './components/admin/pages/AdminPayments'
|
||||
import { AdminUsers } from './components/admin/pages/AdminUsers'
|
||||
import { AdminSettings } from './components/admin/pages/AdminSettings'
|
||||
import { AdminSettings } from './components/admin/pages/AdminSettingsNew'
|
||||
import { Profile } from './components/pages/Profile'
|
||||
import { Loader2 } from 'lucide-react'
|
||||
import { setupAxiosInterceptors } from './utils/axiosSetup'
|
||||
|
||||
function ProtectedRoute({ children }: { children: React.ReactNode }) {
|
||||
const { user, loading } = useAuth()
|
||||
@@ -50,13 +53,35 @@ function PublicRoute({ children }: { children: React.ReactNode }) {
|
||||
}
|
||||
|
||||
if (user) {
|
||||
return <Navigate to="/" replace />
|
||||
// Redirect based on role
|
||||
const redirectTo = user.role === 'admin' ? '/admin' : '/'
|
||||
return <Navigate to={redirectTo} replace />
|
||||
}
|
||||
|
||||
return <>{children}</>
|
||||
}
|
||||
|
||||
export default function App() {
|
||||
const [maintenanceMode, setMaintenanceMode] = useState(false)
|
||||
const [maintenanceMessage, setMaintenanceMessage] = useState('')
|
||||
|
||||
useEffect(() => {
|
||||
// Setup axios interceptor for maintenance mode
|
||||
setupAxiosInterceptors((message) => {
|
||||
setMaintenanceMessage(message)
|
||||
setMaintenanceMode(true)
|
||||
})
|
||||
}, [])
|
||||
|
||||
// Show maintenance page if maintenance mode is active
|
||||
if (maintenanceMode) {
|
||||
return (
|
||||
<ThemeProvider defaultTheme="light" storageKey="tabungin-ui-theme">
|
||||
<MaintenancePage message={maintenanceMessage} />
|
||||
</ThemeProvider>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<BrowserRouter>
|
||||
<ThemeProvider defaultTheme="light" storageKey="tabungin-ui-theme">
|
||||
@@ -74,9 +99,9 @@ export default function App() {
|
||||
<Route path="/admin" element={<ProtectedRoute><AdminLayout /></ProtectedRoute>}>
|
||||
<Route index element={<AdminDashboard />} />
|
||||
<Route path="plans" element={<AdminPlans />} />
|
||||
<Route path="payment-methods" element={<AdminPaymentMethods />} />
|
||||
<Route path="payments" element={<AdminPayments />} />
|
||||
<Route path="users" element={<AdminUsers />} />
|
||||
<Route path="profile" element={<Profile />} />
|
||||
<Route path="settings" element={<AdminSettings />} />
|
||||
</Route>
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { useState, useCallback } from "react"
|
||||
import { Routes, Route, useLocation, useNavigate } from "react-router-dom"
|
||||
import { Routes, Route, useLocation, useNavigate, Navigate } from "react-router-dom"
|
||||
import { useAuth } from "@/contexts/AuthContext"
|
||||
import { DashboardLayout } from "./layout/DashboardLayout"
|
||||
import { Overview } from "./pages/Overview"
|
||||
import { Wallets } from "./pages/Wallets"
|
||||
@@ -7,8 +8,14 @@ import { Transactions } from "./pages/Transactions"
|
||||
import { Profile } from "./pages/Profile"
|
||||
|
||||
export function Dashboard() {
|
||||
const { user } = useAuth()
|
||||
const location = useLocation()
|
||||
const navigate = useNavigate()
|
||||
|
||||
// Block admins from accessing member dashboard
|
||||
if (user?.role === 'admin') {
|
||||
return <Navigate to="/admin" replace />
|
||||
}
|
||||
const [fabWalletDialogOpen, setFabWalletDialogOpen] = useState(false)
|
||||
const [fabTransactionDialogOpen, setFabTransactionDialogOpen] = useState(false)
|
||||
|
||||
|
||||
@@ -16,16 +16,16 @@ export function AdminLayout() {
|
||||
<div className="min-h-screen flex items-center justify-center bg-background">
|
||||
<div className="text-center">
|
||||
<h1 className="text-2xl font-bold text-foreground mb-2">
|
||||
Akses Ditolak
|
||||
Access Denied
|
||||
</h1>
|
||||
<p className="text-muted-foreground">
|
||||
Anda tidak memiliki izin untuk mengakses panel admin.
|
||||
You don't have permission to access the admin panel.
|
||||
</p>
|
||||
<Link
|
||||
to="/"
|
||||
className="mt-4 inline-block text-primary hover:text-primary/90"
|
||||
>
|
||||
Kembali ke Dashboard
|
||||
Back to Dashboard
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { LayoutDashboard, CreditCard, Wallet, Users, Settings, LogOut } from 'lucide-react'
|
||||
import { LayoutDashboard, CreditCard, Wallet, Users, Settings, LogOut, UserCircle } from 'lucide-react'
|
||||
import { Logo } from '../Logo'
|
||||
import {
|
||||
Sidebar,
|
||||
@@ -26,21 +26,21 @@ const items = [
|
||||
url: '/admin/plans',
|
||||
icon: CreditCard,
|
||||
},
|
||||
{
|
||||
title: 'Payment Methods',
|
||||
url: '/admin/payment-methods',
|
||||
icon: Wallet,
|
||||
},
|
||||
{
|
||||
title: 'Payments',
|
||||
url: '/admin/payments',
|
||||
icon: CreditCard,
|
||||
icon: Wallet,
|
||||
},
|
||||
{
|
||||
title: 'Users',
|
||||
url: '/admin/users',
|
||||
icon: Users,
|
||||
},
|
||||
{
|
||||
title: 'Profile',
|
||||
url: '/admin/profile',
|
||||
icon: UserCircle,
|
||||
},
|
||||
{
|
||||
title: 'Settings',
|
||||
url: '/admin/settings',
|
||||
|
||||
@@ -115,7 +115,7 @@ export function AdminDashboard() {
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="flex items-center justify-center h-64">
|
||||
<div className="text-muted-foreground">Memuat...</div>
|
||||
<div className="text-muted-foreground">Loading...</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -127,7 +127,7 @@ export function AdminDashboard() {
|
||||
<div>
|
||||
<h1 className="text-3xl font-bold text-foreground">Dashboard</h1>
|
||||
<p className="mt-2 text-muted-foreground">
|
||||
Selamat datang di panel admin - Overview performa aplikasi
|
||||
Welcome to admin panel - Application performance overview
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -143,7 +143,7 @@ export function AdminDashboard() {
|
||||
<p className="text-xs text-muted-foreground flex items-center mt-1">
|
||||
<ArrowUpRight className="h-3 w-3 text-green-600 mr-1" />
|
||||
<span className="text-green-600">+{stats?.userGrowth || 0}%</span>
|
||||
<span className="ml-1">dari bulan lalu</span>
|
||||
<span className="ml-1">from last month</span>
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
@@ -156,7 +156,7 @@ export function AdminDashboard() {
|
||||
<CardContent>
|
||||
<div className="text-2xl font-bold">{stats?.activeSubscriptions || 0}</div>
|
||||
<p className="text-xs text-muted-foreground mt-1">
|
||||
Langganan aktif saat ini
|
||||
Currently active subscriptions
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
@@ -171,7 +171,7 @@ export function AdminDashboard() {
|
||||
<p className="text-xs text-muted-foreground flex items-center mt-1">
|
||||
<ArrowUpRight className="h-3 w-3 text-green-600 mr-1" />
|
||||
<span className="text-green-600">+{stats?.revenueGrowth || 0}%</span>
|
||||
<span className="ml-1">dari bulan lalu</span>
|
||||
<span className="ml-1">from last month</span>
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
@@ -184,7 +184,7 @@ export function AdminDashboard() {
|
||||
<CardContent>
|
||||
<div className="text-2xl font-bold">{pendingPayments}</div>
|
||||
<p className="text-xs text-muted-foreground mt-1">
|
||||
Menunggu verifikasi
|
||||
Awaiting verification
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
@@ -196,7 +196,7 @@ export function AdminDashboard() {
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Revenue Overview</CardTitle>
|
||||
<CardDescription>Pendapatan 6 bulan terakhir</CardDescription>
|
||||
<CardDescription>Revenue for the last 6 months</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<ResponsiveContainer width="100%" height={300}>
|
||||
@@ -228,7 +228,7 @@ export function AdminDashboard() {
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Subscription Distribution</CardTitle>
|
||||
<CardDescription>Distribusi pengguna per plan</CardDescription>
|
||||
<CardDescription>User distribution by plan</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<ResponsiveContainer width="100%" height={300}>
|
||||
@@ -256,19 +256,19 @@ export function AdminDashboard() {
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Quick Actions</CardTitle>
|
||||
<CardDescription>Akses cepat ke fitur utama</CardDescription>
|
||||
<CardDescription>Quick access to main features</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="grid gap-2">
|
||||
<Button variant="outline" className="justify-start" asChild>
|
||||
<a href="/admin/plans">
|
||||
<CreditCard className="h-4 w-4 mr-2" />
|
||||
Kelola Plans
|
||||
Manage Plans
|
||||
</a>
|
||||
</Button>
|
||||
<Button variant="outline" className="justify-start" asChild>
|
||||
<a href="/admin/payments">
|
||||
<DollarSign className="h-4 w-4 mr-2" />
|
||||
Verifikasi Pembayaran
|
||||
Verify Payments
|
||||
{pendingPayments > 0 && (
|
||||
<Badge variant="destructive" className="ml-auto">{pendingPayments}</Badge>
|
||||
)}
|
||||
@@ -277,13 +277,13 @@ export function AdminDashboard() {
|
||||
<Button variant="outline" className="justify-start" asChild>
|
||||
<a href="/admin/users">
|
||||
<Users className="h-4 w-4 mr-2" />
|
||||
Kelola Users
|
||||
Manage Users
|
||||
</a>
|
||||
</Button>
|
||||
<Button variant="outline" className="justify-start" asChild>
|
||||
<a href="/admin/payment-methods">
|
||||
<Wallet className="h-4 w-4 mr-2" />
|
||||
Metode Pembayaran
|
||||
Payment Methods
|
||||
</a>
|
||||
</Button>
|
||||
</CardContent>
|
||||
@@ -293,7 +293,7 @@ export function AdminDashboard() {
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>System Status</CardTitle>
|
||||
<CardDescription>Status sistem dan statistik</CardDescription>
|
||||
<CardDescription>System status and statistics</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
<div className="flex items-center justify-between">
|
||||
|
||||
@@ -171,7 +171,7 @@ function SortableMethodCard({ method, onEdit, onDelete, onToggleActive, getTypeI
|
||||
{/* Instructions */}
|
||||
{method.instructions && (
|
||||
<div className="mb-6 p-4 rounded-lg bg-muted/50 border border-border/50">
|
||||
<p className="text-xs text-muted-foreground mb-1">Instruksi:</p>
|
||||
<p className="text-xs text-muted-foreground mb-1">Instruction:</p>
|
||||
<p className="text-sm text-foreground line-clamp-3">
|
||||
{method.instructions}
|
||||
</p>
|
||||
@@ -230,7 +230,7 @@ function SortableMethodCard({ method, onEdit, onDelete, onToggleActive, getTypeI
|
||||
<button
|
||||
onClick={() => onDelete(method.id)}
|
||||
className="p-2.5 rounded-lg bg-destructive/10 text-destructive hover:bg-destructive hover:text-destructive-foreground transition-all"
|
||||
title="Hapus"
|
||||
title="Delete"
|
||||
>
|
||||
<Trash2 className="h-4 w-4" />
|
||||
</button>
|
||||
@@ -301,10 +301,10 @@ export function AdminPaymentMethods() {
|
||||
{ methodIds: newMethods.map((m) => m.id) },
|
||||
{ headers: { Authorization: `Bearer ${token}` } }
|
||||
)
|
||||
toast.success('Urutan metode pembayaran berhasil diubah')
|
||||
toast.success('Payment method order updated successfully')
|
||||
} catch (error) {
|
||||
console.error('Failed to reorder methods:', error)
|
||||
toast.error('Gagal mengubah urutan metode pembayaran')
|
||||
toast.error('Failed to update payment method order')
|
||||
fetchMethods() // Revert on error
|
||||
}
|
||||
}
|
||||
@@ -319,11 +319,11 @@ export function AdminPaymentMethods() {
|
||||
{ ...method, isActive: !method.isActive },
|
||||
{ headers: { Authorization: `Bearer ${token}` } }
|
||||
)
|
||||
toast.success(method.isActive ? 'Metode pembayaran dinonaktifkan' : 'Metode pembayaran diaktifkan')
|
||||
toast.success(method.isActive ? 'Payment method deactivated' : 'Payment method activated')
|
||||
fetchMethods()
|
||||
} catch (error) {
|
||||
console.error('Failed to toggle status:', error)
|
||||
toast.error('Gagal mengubah status')
|
||||
toast.error('Failed to change status')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -337,12 +337,12 @@ export function AdminPaymentMethods() {
|
||||
await axios.delete(`${API_URL}/api/admin/payment-methods/${deleteDialog.methodId}`, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
})
|
||||
toast.success('Metode pembayaran berhasil dihapus')
|
||||
toast.success('Payment method deleted successfully')
|
||||
fetchMethods()
|
||||
setDeleteDialog({ open: false, methodId: '' })
|
||||
} catch (error) {
|
||||
console.error('Failed to delete payment method:', error)
|
||||
toast.error('Gagal menghapus metode pembayaran')
|
||||
toast.error('Failed to delete payment method')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -391,13 +391,13 @@ export function AdminPaymentMethods() {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
})
|
||||
}
|
||||
toast.success(editingMethod ? 'Metode pembayaran berhasil diupdate' : 'Metode pembayaran berhasil ditambahkan')
|
||||
toast.success(editingMethod ? 'Payment method updated successfully' : 'Payment method added successfully')
|
||||
fetchMethods()
|
||||
setShowModal(false)
|
||||
setEditingMethod(null)
|
||||
} catch (error) {
|
||||
console.error('Failed to save payment method:', error)
|
||||
toast.error('Gagal menyimpan metode pembayaran')
|
||||
toast.error('Failed to save payment method')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -443,7 +443,7 @@ export function AdminPaymentMethods() {
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="flex items-center justify-center h-64">
|
||||
<div className="text-muted-foreground">Memuat...</div>
|
||||
<div className="text-muted-foreground">Loading...</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -452,16 +452,16 @@ export function AdminPaymentMethods() {
|
||||
<div>
|
||||
<div className="flex items-center justify-between mb-8">
|
||||
<div>
|
||||
<h1 className="text-3xl font-bold text-foreground">
|
||||
Metode Pembayaran
|
||||
</h1>
|
||||
<h4 className="text-xl font-bold text-foreground">
|
||||
Payment Methods
|
||||
</h4>
|
||||
<p className="mt-2 text-muted-foreground">
|
||||
Kelola metode pembayaran yang tersedia
|
||||
Manage available payment methods
|
||||
</p>
|
||||
</div>
|
||||
<Button onClick={() => handleOpenModal()}>
|
||||
<Plus className="h-5 w-5 mr-2" />
|
||||
Tambah Metode
|
||||
Add Method
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
@@ -487,7 +487,7 @@ export function AdminPaymentMethods() {
|
||||
|
||||
{methods.length === 0 && (
|
||||
<div className="text-center py-12">
|
||||
<p className="text-muted-foreground">Belum ada metode pembayaran</p>
|
||||
<p className="text-muted-foreground">No payment methods yet</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -495,21 +495,21 @@ export function AdminPaymentMethods() {
|
||||
<Dialog open={showModal} onOpenChange={setShowModal}>
|
||||
<DialogContent className="max-w-2xl max-h-[90vh] overflow-y-auto">
|
||||
<DialogHeader>
|
||||
<DialogTitle>{editingMethod ? 'Edit Metode Pembayaran' : 'Tambah Metode Pembayaran'}</DialogTitle>
|
||||
<DialogTitle>{editingMethod ? 'Edit Payment Method' : 'Add Payment Method'}</DialogTitle>
|
||||
<DialogDescription>
|
||||
{editingMethod ? 'Ubah informasi metode pembayaran' : 'Tambah metode pembayaran baru'}
|
||||
{editingMethod ? 'Update payment method information' : 'Add a new payment method'}
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
<form onSubmit={handleSubmit} className="space-y-4">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="displayName">Nama Tampilan</Label>
|
||||
<Label htmlFor="displayName">Display Name</Label>
|
||||
<Input
|
||||
id="displayName"
|
||||
required
|
||||
value={formData.displayName}
|
||||
onChange={(e) => setFormData({ ...formData, displayName: e.target.value })}
|
||||
placeholder="BCA, GoPay, QRIS, dll"
|
||||
placeholder="BCA, GoPay, QRIS, etc"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -520,28 +520,28 @@ export function AdminPaymentMethods() {
|
||||
required
|
||||
value={formData.provider}
|
||||
onChange={(e) => setFormData({ ...formData, provider: e.target.value })}
|
||||
placeholder="BCA, Gopay, OVO, dll"
|
||||
placeholder="BCA, Gopay, OVO, etc"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="type">Tipe</Label>
|
||||
<Label htmlFor="type">Type</Label>
|
||||
<Select value={formData.type} onValueChange={(value) => setFormData({ ...formData, type: value })}>
|
||||
<SelectTrigger id="type">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="bank_transfer">Transfer Bank</SelectItem>
|
||||
<SelectItem value="bank_transfer">Bank Transfer</SelectItem>
|
||||
<SelectItem value="ewallet">E-Wallet</SelectItem>
|
||||
<SelectItem value="qris">QRIS</SelectItem>
|
||||
<SelectItem value="other">Lainnya</SelectItem>
|
||||
<SelectItem value="other">Other</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="accountNumber">Nomor Rekening / Akun</Label>
|
||||
<Label htmlFor="accountNumber">Account Number</Label>
|
||||
<Input
|
||||
id="accountNumber"
|
||||
value={formData.accountNumber}
|
||||
@@ -550,7 +550,7 @@ export function AdminPaymentMethods() {
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="accountName">Nama Pemilik</Label>
|
||||
<Label htmlFor="accountName">Account Holder Name</Label>
|
||||
<Input
|
||||
id="accountName"
|
||||
value={formData.accountName}
|
||||
@@ -561,13 +561,13 @@ export function AdminPaymentMethods() {
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="instructions">Instruksi Pembayaran</Label>
|
||||
<Label htmlFor="instructions">Payment Instruction</Label>
|
||||
<Textarea
|
||||
id="instructions"
|
||||
value={formData.instructions}
|
||||
onChange={(e) => setFormData({ ...formData, instructions: e.target.value })}
|
||||
rows={4}
|
||||
placeholder="Petunjuk cara melakukan pembayaran..."
|
||||
placeholder="Detail payment instruction, step by step to make payment..."
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -591,17 +591,17 @@ export function AdminPaymentMethods() {
|
||||
/>
|
||||
<Label htmlFor="isActive" className="cursor-pointer flex justify-between w-full">
|
||||
<div className="font-semibold text-foreground">Active</div>
|
||||
<div className="text-xs text-muted-foreground">Metode pembayaran dapat digunakan</div>
|
||||
<div className="text-xs text-muted-foreground">Payment method can be used</div>
|
||||
</Label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<DialogFooter>
|
||||
<Button type="button" variant="outline" onClick={() => setShowModal(false)}>
|
||||
Batal
|
||||
Cancel
|
||||
</Button>
|
||||
<Button type="submit">
|
||||
{editingMethod ? 'Update' : 'Tambah'}
|
||||
{editingMethod ? 'Update' : 'Add'}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</form>
|
||||
@@ -612,15 +612,15 @@ export function AdminPaymentMethods() {
|
||||
<AlertDialog open={deleteDialog.open} onOpenChange={(open) => setDeleteDialog({ ...deleteDialog, open })}>
|
||||
<AlertDialogContent>
|
||||
<AlertDialogHeader>
|
||||
<AlertDialogTitle>Hapus Metode Pembayaran?</AlertDialogTitle>
|
||||
<AlertDialogTitle>Delete Payment Method?</AlertDialogTitle>
|
||||
<AlertDialogDescription>
|
||||
Apakah Anda yakin ingin menghapus metode pembayaran ini? Tindakan ini tidak dapat dibatalkan.
|
||||
Are you sure you want to delete this payment method? This action cannot be undone.
|
||||
</AlertDialogDescription>
|
||||
</AlertDialogHeader>
|
||||
<AlertDialogFooter>
|
||||
<AlertDialogCancel>Batal</AlertDialogCancel>
|
||||
<AlertDialogCancel>Cancel</AlertDialogCancel>
|
||||
<AlertDialogAction onClick={handleDelete} className="bg-destructive text-destructive-foreground hover:bg-destructive/90">
|
||||
Hapus
|
||||
Delete
|
||||
</AlertDialogAction>
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
|
||||
@@ -68,7 +68,7 @@ export function AdminPayments() {
|
||||
}
|
||||
|
||||
const handleVerify = async (paymentId: string) => {
|
||||
const notes = prompt('Catatan verifikasi (opsional):')
|
||||
const notes = prompt('Verification notes (optional):')
|
||||
if (notes === null) return
|
||||
|
||||
try {
|
||||
@@ -78,16 +78,16 @@ export function AdminPayments() {
|
||||
{ notes },
|
||||
{ headers: { Authorization: `Bearer ${token}` } }
|
||||
)
|
||||
toast.success('Pembayaran berhasil diverifikasi')
|
||||
toast.success('Payment verified successfully')
|
||||
fetchPayments()
|
||||
} catch (error) {
|
||||
console.error('Failed to verify payment:', error)
|
||||
toast.error('Gagal memverifikasi pembayaran')
|
||||
toast.error('Failed to verify payment')
|
||||
}
|
||||
}
|
||||
|
||||
const handleReject = async (paymentId: string) => {
|
||||
const reason = prompt('Alasan penolakan:')
|
||||
const reason = prompt('Rejection reason:')
|
||||
if (!reason) return
|
||||
|
||||
try {
|
||||
@@ -97,11 +97,11 @@ export function AdminPayments() {
|
||||
{ reason },
|
||||
{ headers: { Authorization: `Bearer ${token}` } }
|
||||
)
|
||||
toast.success('Pembayaran berhasil ditolak')
|
||||
toast.success('Payment rejected successfully')
|
||||
fetchPayments()
|
||||
} catch (error) {
|
||||
console.error('Failed to reject payment:', error)
|
||||
toast.error('Gagal menolak pembayaran')
|
||||
toast.error('Failed to reject payment')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -154,7 +154,7 @@ export function AdminPayments() {
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="flex items-center justify-center h-64">
|
||||
<div className="text-muted-foreground">Memuat...</div>
|
||||
<div className="text-muted-foreground">Loading...</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -162,9 +162,9 @@ export function AdminPayments() {
|
||||
return (
|
||||
<div>
|
||||
<div className="mb-8">
|
||||
<h1 className="text-3xl font-bold text-foreground">Verifikasi Pembayaran</h1>
|
||||
<h1 className="text-3xl font-bold text-foreground">Payment Verification</h1>
|
||||
<p className="mt-2 text-muted-foreground">
|
||||
Kelola dan verifikasi bukti pembayaran dari pengguna
|
||||
Manage and verify payment proofs from users
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -190,7 +190,7 @@ export function AdminPayments() {
|
||||
onChange={(e) => setFilter(e.target.value as FilterStatus)}
|
||||
className="px-4 py-2 border border-input rounded-lg bg-background text-foreground focus:ring-2 focus:ring-ring focus:border-transparent"
|
||||
>
|
||||
<option value="all">Semua Status</option>
|
||||
<option value="all">All Status</option>
|
||||
<option value="pending">Pending</option>
|
||||
<option value="verified">Verified</option>
|
||||
<option value="rejected">Rejected</option>
|
||||
@@ -211,16 +211,16 @@ export function AdminPayments() {
|
||||
Plan
|
||||
</th>
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-muted-foreground uppercase tracking-wider">
|
||||
Jumlah
|
||||
Amount
|
||||
</th>
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-muted-foreground uppercase tracking-wider">
|
||||
Metode
|
||||
Method
|
||||
</th>
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-muted-foreground uppercase tracking-wider">
|
||||
Status
|
||||
</th>
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-muted-foreground uppercase tracking-wider">
|
||||
Tanggal
|
||||
Date
|
||||
</th>
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-muted-foreground uppercase tracking-wider">
|
||||
Actions
|
||||
@@ -272,7 +272,7 @@ export function AdminPayments() {
|
||||
<button
|
||||
onClick={() => setSelectedPayment(payment)}
|
||||
className="p-2 rounded-lg text-primary hover:bg-primary/10 transition-colors"
|
||||
title="Lihat Bukti"
|
||||
title="View Proof"
|
||||
>
|
||||
<Eye className="h-4 w-4" />
|
||||
</button>
|
||||
@@ -282,14 +282,14 @@ export function AdminPayments() {
|
||||
<button
|
||||
onClick={() => handleVerify(payment.id)}
|
||||
className="p-2 rounded-lg text-green-600 hover:bg-green-500/10 transition-colors"
|
||||
title="Verifikasi"
|
||||
title="Verify"
|
||||
>
|
||||
<Check className="h-4 w-4" />
|
||||
</button>
|
||||
<button
|
||||
onClick={() => handleReject(payment.id)}
|
||||
className="p-2 rounded-lg text-destructive hover:bg-destructive/10 transition-colors"
|
||||
title="Tolak"
|
||||
title="Reject"
|
||||
>
|
||||
<X className="h-4 w-4" />
|
||||
</button>
|
||||
@@ -321,7 +321,7 @@ export function AdminPayments() {
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<div className="p-6 border-b border-border">
|
||||
<h2 className="text-xl font-bold text-foreground">Bukti Pembayaran</h2>
|
||||
<h2 className="text-xl font-bold text-foreground">Payment Proof</h2>
|
||||
<p className="text-sm text-muted-foreground mt-1">
|
||||
{selectedPayment.user.name} - {selectedPayment.plan.name}
|
||||
</p>
|
||||
@@ -330,15 +330,15 @@ export function AdminPayments() {
|
||||
{selectedPayment.proofUrl ? (
|
||||
<img
|
||||
src={selectedPayment.proofUrl}
|
||||
alt="Bukti Pembayaran"
|
||||
alt="Payment Proof"
|
||||
className="w-full rounded-lg"
|
||||
/>
|
||||
) : (
|
||||
<p className="text-muted-foreground">Tidak ada bukti pembayaran</p>
|
||||
<p className="text-muted-foreground">No payment proof</p>
|
||||
)}
|
||||
{selectedPayment.notes && (
|
||||
<div className="mt-4 p-4 bg-muted rounded-lg">
|
||||
<p className="text-sm font-medium text-foreground">Catatan:</p>
|
||||
<p className="text-sm font-medium text-foreground">Notes:</p>
|
||||
<p className="text-sm text-muted-foreground mt-1">
|
||||
{selectedPayment.notes}
|
||||
</p>
|
||||
@@ -361,7 +361,7 @@ export function AdminPayments() {
|
||||
}}
|
||||
className="px-4 py-2 rounded-lg bg-green-500 text-white hover:bg-green-600 transition-colors"
|
||||
>
|
||||
Verifikasi
|
||||
Verify
|
||||
</button>
|
||||
<button
|
||||
onClick={() => {
|
||||
@@ -370,7 +370,7 @@ export function AdminPayments() {
|
||||
}}
|
||||
className="px-4 py-2 rounded-lg bg-destructive text-destructive-foreground hover:bg-destructive/90 transition-colors"
|
||||
>
|
||||
Tolak
|
||||
Reject
|
||||
</button>
|
||||
</>
|
||||
)}
|
||||
|
||||
@@ -242,7 +242,7 @@ function SortablePlanCard({ plan, onEdit, onDelete, onToggleVisibility, formatPr
|
||||
<button
|
||||
onClick={() => onDelete(plan.id)}
|
||||
className="p-2.5 rounded-lg bg-destructive/10 text-destructive hover:bg-destructive hover:text-destructive-foreground transition-all"
|
||||
title="Hapus"
|
||||
title="Delete"
|
||||
>
|
||||
<Trash2 className="h-4 w-4" />
|
||||
</button>
|
||||
@@ -317,10 +317,10 @@ export function AdminPlans() {
|
||||
{ planIds: newPlans.map((p) => p.id) },
|
||||
{ headers: { Authorization: `Bearer ${token}` } }
|
||||
)
|
||||
toast.success('Urutan plan berhasil diubah')
|
||||
toast.success('Plan order updated successfully')
|
||||
} catch (error) {
|
||||
console.error('Failed to reorder plans:', error)
|
||||
toast.error('Gagal mengubah urutan plan')
|
||||
toast.error('Failed to update plan order')
|
||||
fetchPlans() // Revert on error
|
||||
}
|
||||
}
|
||||
@@ -341,14 +341,14 @@ export function AdminPlans() {
|
||||
if (response.data.action === 'deactivated') {
|
||||
toast.warning(response.data.message)
|
||||
} else {
|
||||
toast.success('Plan berhasil dihapus')
|
||||
toast.success('Plan deleted successfully')
|
||||
}
|
||||
|
||||
fetchPlans()
|
||||
setDeleteDialog({ open: false, planId: '' })
|
||||
} catch (error) {
|
||||
console.error('Failed to delete plan:', error)
|
||||
toast.error('Gagal menghapus plan')
|
||||
toast.error('Failed to delete plan')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -360,11 +360,11 @@ export function AdminPlans() {
|
||||
{ isVisible: !plan.isVisible },
|
||||
{ headers: { Authorization: `Bearer ${token}` } }
|
||||
)
|
||||
toast.success(plan.isVisible ? 'Plan berhasil disembunyikan' : 'Plan berhasil ditampilkan')
|
||||
toast.success(plan.isVisible ? 'Plan hidden successfully' : 'Plan shown successfully')
|
||||
fetchPlans()
|
||||
} catch (error) {
|
||||
console.error('Failed to update plan:', error)
|
||||
toast.error('Gagal mengubah visibilitas plan')
|
||||
toast.error('Failed to change plan visibility')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -423,13 +423,13 @@ export function AdminPlans() {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
})
|
||||
}
|
||||
toast.success(editingPlan ? 'Plan berhasil diupdate' : 'Plan berhasil ditambahkan')
|
||||
toast.success(editingPlan ? 'Plan updated successfully' : 'Plan added successfully')
|
||||
fetchPlans()
|
||||
setShowModal(false)
|
||||
setEditingPlan(null)
|
||||
} catch (error) {
|
||||
console.error('Failed to save plan:', error)
|
||||
toast.error('Gagal menyimpan plan')
|
||||
toast.error('Failed to save plan')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -446,7 +446,7 @@ export function AdminPlans() {
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="flex items-center justify-center h-64">
|
||||
<div className="text-muted-foreground">Memuat...</div>
|
||||
<div className="text-muted-foreground">Loading...</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -455,12 +455,12 @@ export function AdminPlans() {
|
||||
<div>
|
||||
<div className="flex items-center justify-between mb-8">
|
||||
<div>
|
||||
<h1 className="text-3xl font-bold text-foreground">Kelola Plans</h1>
|
||||
<p className="mt-2 text-muted-foreground">Kelola paket berlangganan</p>
|
||||
<h1 className="text-3xl font-bold text-foreground">Manage Plans</h1>
|
||||
<p className="mt-2 text-muted-foreground">Manage subscription plans</p>
|
||||
</div>
|
||||
<Button onClick={() => handleOpenModal()}>
|
||||
<Plus className="h-5 w-5 mr-2" />
|
||||
Tambah Plan
|
||||
Add Plan
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
@@ -486,16 +486,16 @@ export function AdminPlans() {
|
||||
<Dialog open={showModal} onOpenChange={setShowModal}>
|
||||
<DialogContent className="max-w-2xl max-h-[90vh] overflow-y-auto">
|
||||
<DialogHeader>
|
||||
<DialogTitle>{editingPlan ? 'Edit Plan' : 'Tambah Plan Baru'}</DialogTitle>
|
||||
<DialogTitle>{editingPlan ? 'Edit Plan' : 'Add New Plan'}</DialogTitle>
|
||||
<DialogDescription>
|
||||
{editingPlan ? 'Ubah informasi plan berlangganan' : 'Buat plan berlangganan baru'}
|
||||
{editingPlan ? 'Update subscription plan information' : 'Create a new subscription plan'}
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
<form onSubmit={handleSubmit} className="space-y-4">
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="name">Nama Plan</Label>
|
||||
<Label htmlFor="name">Plan Name</Label>
|
||||
<Input
|
||||
id="name"
|
||||
required
|
||||
@@ -515,7 +515,7 @@ export function AdminPlans() {
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="description">Deskripsi</Label>
|
||||
<Label htmlFor="description">Description</Label>
|
||||
<Textarea
|
||||
id="description"
|
||||
required
|
||||
@@ -527,7 +527,7 @@ export function AdminPlans() {
|
||||
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="price">Harga</Label>
|
||||
<Label htmlFor="price">Price</Label>
|
||||
<Input
|
||||
id="price"
|
||||
type="number"
|
||||
@@ -552,7 +552,7 @@ export function AdminPlans() {
|
||||
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="durationType">Tipe Durasi</Label>
|
||||
<Label htmlFor="durationType">Duration Type</Label>
|
||||
<Select value={formData.durationType} onValueChange={(value) => setFormData({ ...formData, durationType: value })}>
|
||||
<SelectTrigger id="durationType">
|
||||
<SelectValue />
|
||||
@@ -576,7 +576,7 @@ export function AdminPlans() {
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="features">Features (satu per baris)</Label>
|
||||
<Label htmlFor="features">Features (one per line)</Label>
|
||||
<Textarea
|
||||
id="features"
|
||||
value={formData.features.join('\n')}
|
||||
@@ -636,7 +636,7 @@ export function AdminPlans() {
|
||||
/>
|
||||
<Label htmlFor="isVisible" className="cursor-pointer flex justify-between w-full">
|
||||
<div className="font-semibold text-foreground">Visible</div>
|
||||
<div className="text-xs text-muted-foreground">Tampil di halaman pricing</div>
|
||||
<div className="text-xs text-muted-foreground">Show on pricing page</div>
|
||||
</Label>
|
||||
</div>
|
||||
</div>
|
||||
@@ -644,10 +644,10 @@ export function AdminPlans() {
|
||||
|
||||
<DialogFooter>
|
||||
<Button type="button" variant="outline" onClick={() => setShowModal(false)}>
|
||||
Batal
|
||||
Cancel
|
||||
</Button>
|
||||
<Button type="submit">
|
||||
{editingPlan ? 'Update' : 'Tambah'}
|
||||
{editingPlan ? 'Update' : 'Add'}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</form>
|
||||
@@ -658,15 +658,15 @@ export function AdminPlans() {
|
||||
<AlertDialog open={deleteDialog.open} onOpenChange={(open) => setDeleteDialog({ ...deleteDialog, open })}>
|
||||
<AlertDialogContent>
|
||||
<AlertDialogHeader>
|
||||
<AlertDialogTitle>Hapus Plan?</AlertDialogTitle>
|
||||
<AlertDialogTitle>Delete Plan?</AlertDialogTitle>
|
||||
<AlertDialogDescription>
|
||||
Apakah Anda yakin ingin menghapus plan ini? Tindakan ini tidak dapat dibatalkan.
|
||||
Are you sure you want to delete this plan? This action cannot be undone.
|
||||
</AlertDialogDescription>
|
||||
</AlertDialogHeader>
|
||||
<AlertDialogFooter>
|
||||
<AlertDialogCancel>Batal</AlertDialogCancel>
|
||||
<AlertDialogCancel>Cancel</AlertDialogCancel>
|
||||
<AlertDialogAction onClick={handleDelete} className="bg-destructive text-destructive-foreground hover:bg-destructive/90">
|
||||
Hapus
|
||||
Delete
|
||||
</AlertDialogAction>
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
|
||||
@@ -29,7 +29,7 @@ export function AdminSettings() {
|
||||
enableEmailVerification: true,
|
||||
enablePaymentVerification: true,
|
||||
maintenanceMode: false,
|
||||
maintenanceMessage: 'Sistem sedang dalam pemeliharaan. Mohon coba lagi nanti.',
|
||||
maintenanceMessage: 'System is under maintenance. Please try again later.',
|
||||
})
|
||||
const [loading, setLoading] = useState(true)
|
||||
const [saving, setSaving] = useState(false)
|
||||
@@ -65,7 +65,7 @@ export function AdminSettings() {
|
||||
enableEmailVerification: configData.features?.find((c) => c.key === 'enable_email_verification')?.value === 'true',
|
||||
enablePaymentVerification: configData.features?.find((c) => c.key === 'enable_payment_verification')?.value === 'true',
|
||||
maintenanceMode: configData.system?.find((c) => c.key === 'maintenance_mode')?.value === 'true',
|
||||
maintenanceMessage: configData.system?.find((c) => c.key === 'maintenance_message')?.value || 'Sistem sedang dalam pemeliharaan. Mohon coba lagi nanti.',
|
||||
maintenanceMessage: configData.system?.find((c) => c.key === 'maintenance_message')?.value || 'System is under maintenance. Please try again later.',
|
||||
}
|
||||
setSettings(settingsObj)
|
||||
} catch (error) {
|
||||
@@ -82,14 +82,14 @@ export function AdminSettings() {
|
||||
|
||||
// Save each setting individually
|
||||
const configUpdates = [
|
||||
{ key: 'app_name', value: settings.appName, category: 'general', label: 'Nama Aplikasi', type: 'text' },
|
||||
{ key: 'app_url', value: settings.appUrl, category: 'general', label: 'URL Aplikasi', type: 'text' },
|
||||
{ key: 'support_email', value: settings.supportEmail, category: 'general', label: 'Email Support', type: 'email' },
|
||||
{ key: 'enable_registration', value: String(settings.enableRegistration), category: 'features', label: 'Registrasi Pengguna Baru', type: 'boolean' },
|
||||
{ key: 'enable_email_verification', value: String(settings.enableEmailVerification), category: 'features', label: 'Verifikasi Email', type: 'boolean' },
|
||||
{ key: 'enable_payment_verification', value: String(settings.enablePaymentVerification), category: 'features', label: 'Verifikasi Pembayaran', type: 'boolean' },
|
||||
{ key: 'maintenance_mode', value: String(settings.maintenanceMode), category: 'system', label: 'Mode Pemeliharaan', type: 'boolean' },
|
||||
{ key: 'maintenance_message', value: settings.maintenanceMessage, category: 'system', label: 'Pesan Pemeliharaan', type: 'text' },
|
||||
{ key: 'app_name', value: settings.appName, category: 'general', label: 'Application Name', type: 'text' },
|
||||
{ key: 'app_url', value: settings.appUrl, category: 'general', label: 'Application URL', type: 'text' },
|
||||
{ key: 'support_email', value: settings.supportEmail, category: 'general', label: 'Support Email', type: 'email' },
|
||||
{ key: 'enable_registration', value: String(settings.enableRegistration), category: 'features', label: 'New User Registration', type: 'boolean' },
|
||||
{ key: 'enable_email_verification', value: String(settings.enableEmailVerification), category: 'features', label: 'Email Verification', type: 'boolean' },
|
||||
{ key: 'enable_payment_verification', value: String(settings.enablePaymentVerification), category: 'features', label: 'Payment Verification', type: 'boolean' },
|
||||
{ key: 'maintenance_mode', value: String(settings.maintenanceMode), category: 'system', label: 'Maintenance Mode', type: 'boolean' },
|
||||
{ key: 'maintenance_message', value: settings.maintenanceMessage, category: 'system', label: 'Maintenance Message', type: 'text' },
|
||||
]
|
||||
|
||||
await Promise.all(
|
||||
@@ -100,11 +100,11 @@ export function AdminSettings() {
|
||||
)
|
||||
)
|
||||
|
||||
toast.success('Pengaturan berhasil disimpan')
|
||||
toast.success('Settings saved successfully')
|
||||
fetchSettings() // Refresh
|
||||
} catch (error) {
|
||||
console.error('Failed to save settings:', error)
|
||||
toast.error('Gagal menyimpan pengaturan')
|
||||
toast.error('Failed to save settings')
|
||||
} finally {
|
||||
setSaving(false)
|
||||
}
|
||||
@@ -117,7 +117,7 @@ export function AdminSettings() {
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="flex items-center justify-center h-64">
|
||||
<div className="text-muted-foreground">Memuat...</div>
|
||||
<div className="text-muted-foreground">Loading...</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -125,9 +125,9 @@ export function AdminSettings() {
|
||||
return (
|
||||
<div className="max-w-4xl mx-auto">
|
||||
<div className="mb-8">
|
||||
<h1 className="text-3xl font-bold text-foreground">Pengaturan Aplikasi</h1>
|
||||
<h1 className="text-3xl font-bold text-foreground">Application Settings</h1>
|
||||
<p className="mt-2 text-muted-foreground">
|
||||
Kelola konfigurasi dan pengaturan sistem
|
||||
Manage system configuration and settings
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -139,15 +139,15 @@ export function AdminSettings() {
|
||||
<Globe className="h-5 w-5 text-primary" />
|
||||
</div>
|
||||
<div>
|
||||
<h2 className="text-lg font-semibold text-foreground">Pengaturan Umum</h2>
|
||||
<p className="text-sm text-muted-foreground">Informasi dasar aplikasi</p>
|
||||
<h2 className="text-lg font-semibold text-foreground">General Settings</h2>
|
||||
<p className="text-sm text-muted-foreground">Basic application information</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-foreground mb-2">
|
||||
Nama Aplikasi
|
||||
Application Name
|
||||
</label>
|
||||
<Input
|
||||
type="text"
|
||||
@@ -158,7 +158,7 @@ export function AdminSettings() {
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-foreground mb-2">
|
||||
URL Aplikasi
|
||||
Application URL
|
||||
</label>
|
||||
<Input
|
||||
type="url"
|
||||
@@ -187,17 +187,17 @@ export function AdminSettings() {
|
||||
<Shield className="h-5 w-5 text-primary" />
|
||||
</div>
|
||||
<div>
|
||||
<h2 className="text-lg font-semibold text-foreground">Fitur & Keamanan</h2>
|
||||
<p className="text-sm text-muted-foreground">Aktifkan atau nonaktifkan fitur</p>
|
||||
<h2 className="text-lg font-semibold text-foreground">Features & Security</h2>
|
||||
<p className="text-sm text-muted-foreground">Enable or disable features</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center justify-between p-4 rounded-lg bg-muted/50">
|
||||
<div>
|
||||
<p className="font-medium text-foreground">Registrasi Pengguna Baru</p>
|
||||
<p className="font-medium text-foreground">New User Registration</p>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Izinkan pengguna baru mendaftar
|
||||
Allow new users to register
|
||||
</p>
|
||||
</div>
|
||||
<Switch
|
||||
@@ -208,9 +208,9 @@ export function AdminSettings() {
|
||||
|
||||
<div className="flex items-center justify-between p-4 rounded-lg bg-muted/50">
|
||||
<div>
|
||||
<p className="font-medium text-foreground">Verifikasi Email</p>
|
||||
<p className="font-medium text-foreground">Email Verification</p>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Wajibkan verifikasi email untuk pengguna baru
|
||||
Require email verification for new users
|
||||
</p>
|
||||
</div>
|
||||
<Switch
|
||||
@@ -221,9 +221,9 @@ export function AdminSettings() {
|
||||
|
||||
<div className="flex items-center justify-between p-4 rounded-lg bg-muted/50">
|
||||
<div>
|
||||
<p className="font-medium text-foreground">Verifikasi Pembayaran Manual</p>
|
||||
<p className="font-medium text-foreground">Manual Payment Verification</p>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Aktifkan verifikasi manual untuk pembayaran
|
||||
Enable manual verification for payments
|
||||
</p>
|
||||
</div>
|
||||
<Switch
|
||||
@@ -241,9 +241,9 @@ export function AdminSettings() {
|
||||
<Database className="h-5 w-5 text-primary" />
|
||||
</div>
|
||||
<div>
|
||||
<h2 className="text-lg font-semibold text-foreground">Mode Pemeliharaan</h2>
|
||||
<h2 className="text-lg font-semibold text-foreground">Maintenance Mode</h2>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Nonaktifkan akses sementara untuk maintenance
|
||||
Temporarily disable access for maintenance
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -251,9 +251,9 @@ export function AdminSettings() {
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center justify-between p-4 rounded-lg bg-destructive/10 border border-destructive/20">
|
||||
<div>
|
||||
<p className="font-medium text-foreground">Mode Pemeliharaan</p>
|
||||
<p className="font-medium text-foreground">Maintenance Mode</p>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Aktifkan untuk menutup akses sementara
|
||||
Enable to temporarily close access
|
||||
</p>
|
||||
</div>
|
||||
<Switch
|
||||
@@ -266,7 +266,7 @@ export function AdminSettings() {
|
||||
{settings.maintenanceMode && (
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-foreground mb-2">
|
||||
Pesan Pemeliharaan
|
||||
Maintenance Message
|
||||
</label>
|
||||
<Textarea
|
||||
value={settings.maintenanceMessage}
|
||||
@@ -286,7 +286,7 @@ export function AdminSettings() {
|
||||
className="flex items-center gap-2"
|
||||
>
|
||||
<Save className="h-5 w-5" />
|
||||
{saving ? 'Menyimpan...' : 'Simpan Pengaturan'}
|
||||
{saving ? 'Saving...' : 'Save Settings'}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
63
apps/web/src/components/admin/pages/AdminSettingsNew.tsx
Normal file
63
apps/web/src/components/admin/pages/AdminSettingsNew.tsx
Normal file
@@ -0,0 +1,63 @@
|
||||
import { useState } from 'react'
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
|
||||
import { Globe, Shield, Wallet } from 'lucide-react'
|
||||
import { AdminSettingsGeneral } from './settings/AdminSettingsGeneral'
|
||||
import { AdminSettingsSecurity } from './settings/AdminSettingsSecurity'
|
||||
import { AdminSettingsPaymentMethods } from './settings/AdminSettingsPaymentMethods'
|
||||
|
||||
export function AdminSettings() {
|
||||
const [activeTab, setActiveTab] = useState('general')
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<div>
|
||||
<h1 className="text-3xl font-bold text-foreground">Settings</h1>
|
||||
<p className="mt-2 text-muted-foreground">
|
||||
Manage system configuration and settings
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<Tabs value={activeTab} onValueChange={setActiveTab} className="flex flex-col md:flex-row gap-6">
|
||||
{/* Vertical Tabs (Horizontal scrollable on mobile) */}
|
||||
<TabsList className="flex md:flex-col h-auto md:w-64 flex-shrink-0 bg-muted/50 p-1 overflow-x-auto md:overflow-x-visible justify-start md:h-fit">
|
||||
<TabsTrigger
|
||||
value="general"
|
||||
className="w-full justify-start gap-3 px-4 py-3 data-[state=active]:bg-background whitespace-nowrap"
|
||||
>
|
||||
<Globe className="h-5 w-5" />
|
||||
<span className="inline">General</span>
|
||||
</TabsTrigger>
|
||||
<TabsTrigger
|
||||
value="security"
|
||||
className="w-full justify-start gap-3 px-4 py-3 data-[state=active]:bg-background whitespace-nowrap"
|
||||
>
|
||||
<Shield className="h-5 w-5" />
|
||||
<span className="inline">Security</span>
|
||||
</TabsTrigger>
|
||||
<TabsTrigger
|
||||
value="payment-methods"
|
||||
className="w-full justify-start gap-3 px-4 py-3 data-[state=active]:bg-background whitespace-nowrap"
|
||||
>
|
||||
<Wallet className="h-5 w-5" />
|
||||
<span className="inline">Payment Methods</span>
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
|
||||
{/* Tab Content */}
|
||||
<div className="flex-1 min-w-0">
|
||||
<TabsContent value="general" className="mt-0">
|
||||
<AdminSettingsGeneral />
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="security" className="mt-0">
|
||||
<AdminSettingsSecurity />
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="payment-methods" className="mt-0">
|
||||
<AdminSettingsPaymentMethods />
|
||||
</TabsContent>
|
||||
</div>
|
||||
</Tabs>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,10 +1,17 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import axios from 'axios'
|
||||
import { Search, UserX, UserCheck, Crown } from 'lucide-react'
|
||||
import { Search, UserX, UserCheck, Crown, Plus, Edit, Trash2 } from 'lucide-react'
|
||||
import { toast } from 'sonner'
|
||||
import { Input } from '@/components/ui/input'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Label } from '@/components/ui/label'
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from '@/components/ui/select'
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
@@ -39,6 +46,9 @@ export function AdminUsers() {
|
||||
const [suspendReason, setSuspendReason] = useState('')
|
||||
const [grantProDialog, setGrantProDialog] = useState<{ open: boolean; userId: string }>({ open: false, userId: '' })
|
||||
const [proDays, setProDays] = useState('')
|
||||
const [userDialog, setUserDialog] = useState<{ open: boolean; mode: 'create' | 'edit'; user?: User }>({ open: false, mode: 'create' })
|
||||
const [formData, setFormData] = useState({ email: '', password: '', name: '', role: 'user' })
|
||||
const [deleteDialog, setDeleteDialog] = useState<{ open: boolean; userId: string; userName: string }>({ open: false, userId: '', userName: '' })
|
||||
|
||||
useEffect(() => {
|
||||
fetchUsers()
|
||||
@@ -76,12 +86,12 @@ export function AdminUsers() {
|
||||
suspendDialog.suspend ? { reason: suspendReason } : {},
|
||||
{ headers: { Authorization: `Bearer ${token}` } }
|
||||
)
|
||||
toast.success(suspendDialog.suspend ? 'User berhasil disuspend' : 'User berhasil diaktifkan kembali')
|
||||
toast.success(suspendDialog.suspend ? 'User suspended successfully' : 'User unsuspended successfully')
|
||||
fetchUsers()
|
||||
setSuspendDialog({ open: false, userId: '', suspend: false })
|
||||
} catch (error) {
|
||||
console.error('Failed to update user:', error)
|
||||
toast.error('Gagal mengupdate user')
|
||||
toast.error('Failed to update user')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,32 +113,116 @@ export function AdminUsers() {
|
||||
},
|
||||
{ headers: { Authorization: `Bearer ${token}` } }
|
||||
)
|
||||
toast.success('Akses Pro berhasil diberikan!')
|
||||
toast.success('Pro access granted successfully!')
|
||||
fetchUsers()
|
||||
setGrantProDialog({ open: false, userId: '' })
|
||||
} catch (error) {
|
||||
console.error('Failed to grant pro access:', error)
|
||||
toast.error('Gagal memberikan akses Pro')
|
||||
toast.error('Failed to grant Pro access')
|
||||
}
|
||||
}
|
||||
|
||||
const openUserDialog = (mode: 'create' | 'edit', user?: User) => {
|
||||
setUserDialog({ open: true, mode, user })
|
||||
if (mode === 'edit' && user) {
|
||||
setFormData({
|
||||
email: user.email,
|
||||
password: '',
|
||||
name: user.name || '',
|
||||
role: user.role,
|
||||
})
|
||||
} else {
|
||||
setFormData({ email: '', password: '', name: '', role: 'user' })
|
||||
}
|
||||
}
|
||||
|
||||
const handleSaveUser = async () => {
|
||||
if (!formData.email) {
|
||||
toast.error('Email is required')
|
||||
return
|
||||
}
|
||||
|
||||
if (userDialog.mode === 'create' && !formData.password) {
|
||||
toast.error('Password is required')
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const token = localStorage.getItem('token')
|
||||
|
||||
if (userDialog.mode === 'create') {
|
||||
await axios.post(
|
||||
`${API_URL}/api/admin/users`,
|
||||
formData,
|
||||
{ headers: { Authorization: `Bearer ${token}` } }
|
||||
)
|
||||
toast.success('User created successfully!')
|
||||
} else {
|
||||
const updateData: any = {
|
||||
email: formData.email,
|
||||
name: formData.name || null,
|
||||
role: formData.role,
|
||||
}
|
||||
await axios.put(
|
||||
`${API_URL}/api/admin/users/${userDialog.user?.id}`,
|
||||
updateData,
|
||||
{ headers: { Authorization: `Bearer ${token}` } }
|
||||
)
|
||||
toast.success('User updated successfully!')
|
||||
}
|
||||
|
||||
fetchUsers()
|
||||
setUserDialog({ open: false, mode: 'create' })
|
||||
} catch (error: any) {
|
||||
console.error('Failed to save user:', error)
|
||||
const message = error.response?.data?.message || 'Failed to save user'
|
||||
toast.error(message)
|
||||
}
|
||||
}
|
||||
|
||||
const openDeleteDialog = (userId: string, userName: string) => {
|
||||
setDeleteDialog({ open: true, userId, userName })
|
||||
}
|
||||
|
||||
const handleDelete = async () => {
|
||||
try {
|
||||
const token = localStorage.getItem('token')
|
||||
await axios.delete(
|
||||
`${API_URL}/api/admin/users/${deleteDialog.userId}`,
|
||||
{ headers: { Authorization: `Bearer ${token}` } }
|
||||
)
|
||||
toast.success('User deleted successfully!')
|
||||
fetchUsers()
|
||||
setDeleteDialog({ open: false, userId: '', userName: '' })
|
||||
} catch (error) {
|
||||
console.error('Failed to delete user:', error)
|
||||
toast.error('Failed to delete user')
|
||||
}
|
||||
}
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="flex items-center justify-center h-64">
|
||||
<div className="text-muted-foreground">Memuat...</div>
|
||||
<div className="text-muted-foreground">Loading...</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="mb-8">
|
||||
<h1 className="text-3xl font-bold text-foreground">
|
||||
Kelola Users
|
||||
</h1>
|
||||
<p className="mt-2 text-muted-foreground">
|
||||
Kelola akun dan izin pengguna
|
||||
</p>
|
||||
<div className="mb-8 flex items-center justify-between">
|
||||
<div>
|
||||
<h1 className="text-3xl font-bold text-foreground">
|
||||
Manage Users
|
||||
</h1>
|
||||
<p className="mt-2 text-muted-foreground">
|
||||
Manage user accounts and permissions
|
||||
</p>
|
||||
</div>
|
||||
<Button onClick={() => openUserDialog('create')}>
|
||||
<Plus className="h-4 w-4 mr-2" />
|
||||
Add User
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* Search */}
|
||||
@@ -217,6 +311,13 @@ export function AdminUsers() {
|
||||
)}
|
||||
</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium space-x-2">
|
||||
<button
|
||||
onClick={() => openUserDialog('edit', user)}
|
||||
className="text-primary hover:text-primary/80"
|
||||
title="Edit User"
|
||||
>
|
||||
<Edit className="h-4 w-4 inline" />
|
||||
</button>
|
||||
{user.suspendedAt ? (
|
||||
<button
|
||||
onClick={() => openSuspendDialog(user.id, false)}
|
||||
@@ -241,6 +342,13 @@ export function AdminUsers() {
|
||||
>
|
||||
<Crown className="h-4 w-4 inline" />
|
||||
</button>
|
||||
<button
|
||||
onClick={() => openDeleteDialog(user.id, user.name || user.email)}
|
||||
className="text-destructive hover:text-destructive/80"
|
||||
title="Delete User"
|
||||
>
|
||||
<Trash2 className="h-4 w-4 inline" />
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
@@ -251,7 +359,7 @@ export function AdminUsers() {
|
||||
|
||||
{users.length === 0 && (
|
||||
<div className="text-center py-12">
|
||||
<p className="text-muted-foreground">Tidak ada user</p>
|
||||
<p className="text-muted-foreground">No users found</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -317,6 +425,93 @@ export function AdminUsers() {
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
|
||||
{/* Create/Edit User Dialog */}
|
||||
<Dialog open={userDialog.open} onOpenChange={(open) => setUserDialog({ ...userDialog, open })}>
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>{userDialog.mode === 'create' ? 'Create New User' : 'Edit User'}</DialogTitle>
|
||||
<DialogDescription>
|
||||
{userDialog.mode === 'create'
|
||||
? 'Add a new user to the system.'
|
||||
: 'Update user information.'}
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<div className="space-y-4">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="email">Email *</Label>
|
||||
<Input
|
||||
id="email"
|
||||
type="email"
|
||||
value={formData.email}
|
||||
onChange={(e) => setFormData({ ...formData, email: e.target.value })}
|
||||
placeholder="user@example.com"
|
||||
/>
|
||||
</div>
|
||||
{userDialog.mode === 'create' && (
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="password">Password *</Label>
|
||||
<Input
|
||||
id="password"
|
||||
type="password"
|
||||
value={formData.password}
|
||||
onChange={(e) => setFormData({ ...formData, password: e.target.value })}
|
||||
placeholder="Enter password"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="name">Name</Label>
|
||||
<Input
|
||||
id="name"
|
||||
value={formData.name}
|
||||
onChange={(e) => setFormData({ ...formData, name: e.target.value })}
|
||||
placeholder="Full name"
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="role">Role</Label>
|
||||
<Select value={formData.role} onValueChange={(value) => setFormData({ ...formData, role: value })}>
|
||||
<SelectTrigger>
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="user">User</SelectItem>
|
||||
<SelectItem value="admin">Admin</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
<DialogFooter>
|
||||
<Button variant="outline" onClick={() => setUserDialog({ open: false, mode: 'create' })}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button onClick={handleSaveUser}>
|
||||
{userDialog.mode === 'create' ? 'Create User' : 'Update User'}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
|
||||
{/* Delete Confirmation Dialog */}
|
||||
<Dialog open={deleteDialog.open} onOpenChange={(open) => setDeleteDialog({ ...deleteDialog, open })}>
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>Delete User</DialogTitle>
|
||||
<DialogDescription>
|
||||
Are you sure you want to delete <strong>{deleteDialog.userName}</strong>? This action cannot be undone and will delete all associated data including wallets and transactions.
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<DialogFooter>
|
||||
<Button variant="outline" onClick={() => setDeleteDialog({ open: false, userId: '', userName: '' })}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button variant="destructive" onClick={handleDelete}>
|
||||
Delete User
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,209 @@
|
||||
import { useState, useEffect } from 'react'
|
||||
import axios from 'axios'
|
||||
import { Globe, Database, Save } from 'lucide-react'
|
||||
import { toast } from 'sonner'
|
||||
import { Input } from '@/components/ui/input'
|
||||
import { Textarea } from '@/components/ui/textarea'
|
||||
import { Switch } from '@/components/ui/switch'
|
||||
import { Button } from '@/components/ui/button'
|
||||
|
||||
const API_URL = import.meta.env.VITE_API_URL || 'http://localhost:3001'
|
||||
|
||||
interface GeneralSettings {
|
||||
appName: string
|
||||
appUrl: string
|
||||
supportEmail: string
|
||||
maintenanceMode: boolean
|
||||
maintenanceMessage: string
|
||||
}
|
||||
|
||||
export function AdminSettingsGeneral() {
|
||||
const [settings, setSettings] = useState<GeneralSettings>({
|
||||
appName: 'Tabungin',
|
||||
appUrl: 'https://tabungin.app',
|
||||
supportEmail: 'support@tabungin.app',
|
||||
maintenanceMode: false,
|
||||
maintenanceMessage: 'System is under maintenance. Please try again later.',
|
||||
})
|
||||
const [loading, setLoading] = useState(true)
|
||||
const [saving, setSaving] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
fetchSettings()
|
||||
}, [])
|
||||
|
||||
const fetchSettings = async () => {
|
||||
try {
|
||||
setLoading(true)
|
||||
const token = localStorage.getItem('token')
|
||||
const response = await axios.get(`${API_URL}/api/admin/config/by-category`, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
})
|
||||
|
||||
const configData = response.data
|
||||
const settingsObj: GeneralSettings = {
|
||||
appName: configData.general?.find((c: any) => c.key === 'app_name')?.value || 'Tabungin',
|
||||
appUrl: configData.general?.find((c: any) => c.key === 'app_url')?.value || 'https://tabungin.app',
|
||||
supportEmail: configData.general?.find((c: any) => c.key === 'support_email')?.value || 'support@tabungin.app',
|
||||
maintenanceMode: configData.system?.find((c: any) => c.key === 'maintenance_mode')?.value === 'true',
|
||||
maintenanceMessage: configData.system?.find((c: any) => c.key === 'maintenance_message')?.value || 'System is under maintenance. Please try again later.',
|
||||
}
|
||||
setSettings(settingsObj)
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch settings:', error)
|
||||
} finally {
|
||||
setLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
const handleSave = async () => {
|
||||
try {
|
||||
setSaving(true)
|
||||
const token = localStorage.getItem('token')
|
||||
|
||||
const configUpdates = [
|
||||
{ key: 'app_name', value: settings.appName, category: 'general', label: 'Application Name', type: 'text' },
|
||||
{ key: 'app_url', value: settings.appUrl, category: 'general', label: 'Application URL', type: 'text' },
|
||||
{ key: 'support_email', value: settings.supportEmail, category: 'general', label: 'Support Email', type: 'email' },
|
||||
{ key: 'maintenance_mode', value: String(settings.maintenanceMode), category: 'system', label: 'Maintenance Mode', type: 'boolean' },
|
||||
{ key: 'maintenance_message', value: settings.maintenanceMessage, category: 'system', label: 'Maintenance Message', type: 'text' },
|
||||
]
|
||||
|
||||
await Promise.all(
|
||||
configUpdates.map((config) =>
|
||||
axios.post(`${API_URL}/api/admin/config/${config.key}`, config, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
})
|
||||
)
|
||||
)
|
||||
|
||||
toast.success('Settings saved successfully')
|
||||
fetchSettings()
|
||||
} catch (error) {
|
||||
console.error('Failed to save settings:', error)
|
||||
toast.error('Failed to save settings')
|
||||
} finally {
|
||||
setSaving(false)
|
||||
}
|
||||
}
|
||||
|
||||
const handleChange = (field: keyof GeneralSettings, value: string | boolean) => {
|
||||
setSettings((prev) => ({ ...prev, [field]: value }))
|
||||
}
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="flex items-center justify-center h-64">
|
||||
<div className="text-muted-foreground">Loading...</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
{/* General Settings Card */}
|
||||
<div className="bg-card rounded-xl border border-border p-6">
|
||||
<div className="flex items-center gap-3 mb-6">
|
||||
<div className="p-2 rounded-lg bg-primary/10">
|
||||
<Globe className="h-5 w-5 text-primary" />
|
||||
</div>
|
||||
<div>
|
||||
<h2 className="text-lg font-semibold text-foreground">General Settings</h2>
|
||||
<p className="text-sm text-muted-foreground">Basic application information</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-foreground mb-2">
|
||||
Application Name
|
||||
</label>
|
||||
<Input
|
||||
type="text"
|
||||
value={settings.appName}
|
||||
onChange={(e) => handleChange('appName', e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-foreground mb-2">
|
||||
Application URL
|
||||
</label>
|
||||
<Input
|
||||
type="url"
|
||||
value={settings.appUrl}
|
||||
onChange={(e) => handleChange('appUrl', e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-foreground mb-2">
|
||||
Support Email
|
||||
</label>
|
||||
<Input
|
||||
type="email"
|
||||
value={settings.supportEmail}
|
||||
onChange={(e) => handleChange('supportEmail', e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Maintenance Mode Card */}
|
||||
<div className="bg-card rounded-xl border border-border p-6">
|
||||
<div className="flex items-center gap-3 mb-6">
|
||||
<div className="p-2 rounded-lg bg-primary/10">
|
||||
<Database className="h-5 w-5 text-primary" />
|
||||
</div>
|
||||
<div>
|
||||
<h2 className="text-lg font-semibold text-foreground">Maintenance Mode</h2>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Temporarily disable access for maintenance
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center justify-between p-4 rounded-lg bg-destructive/10 border border-destructive/20">
|
||||
<div>
|
||||
<p className="font-medium text-foreground">Maintenance Mode</p>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Enable to temporarily close access
|
||||
</p>
|
||||
</div>
|
||||
<Switch
|
||||
checked={settings.maintenanceMode}
|
||||
onCheckedChange={(checked) => handleChange('maintenanceMode', checked)}
|
||||
className="data-[state=checked]:bg-destructive"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{settings.maintenanceMode && (
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-foreground mb-2">
|
||||
Maintenance Message
|
||||
</label>
|
||||
<Textarea
|
||||
value={settings.maintenanceMessage}
|
||||
onChange={(e) => handleChange('maintenanceMessage', e.target.value)}
|
||||
rows={3}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Save Button */}
|
||||
<div className="flex justify-end">
|
||||
<Button
|
||||
onClick={handleSave}
|
||||
disabled={saving}
|
||||
className="flex items-center gap-2"
|
||||
>
|
||||
<Save className="h-5 w-5" />
|
||||
{saving ? 'Saving...' : 'Save Settings'}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
// This component wraps the existing AdminPaymentMethods component
|
||||
// to be used as a tab content in the Settings page
|
||||
|
||||
import { AdminPaymentMethods } from '../AdminPaymentMethods'
|
||||
|
||||
export function AdminSettingsPaymentMethods() {
|
||||
return <AdminPaymentMethods />
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
import { useState, useEffect } from 'react'
|
||||
import axios from 'axios'
|
||||
import { Shield, Save } from 'lucide-react'
|
||||
import { toast } from 'sonner'
|
||||
import { Switch } from '@/components/ui/switch'
|
||||
import { Button } from '@/components/ui/button'
|
||||
|
||||
const API_URL = import.meta.env.VITE_API_URL || 'http://localhost:3001'
|
||||
|
||||
interface SecuritySettings {
|
||||
enableRegistration: boolean
|
||||
enableEmailVerification: boolean
|
||||
enablePaymentVerification: boolean
|
||||
}
|
||||
|
||||
export function AdminSettingsSecurity() {
|
||||
const [settings, setSettings] = useState<SecuritySettings>({
|
||||
enableRegistration: true,
|
||||
enableEmailVerification: true,
|
||||
enablePaymentVerification: true,
|
||||
})
|
||||
const [loading, setLoading] = useState(true)
|
||||
const [saving, setSaving] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
fetchSettings()
|
||||
}, [])
|
||||
|
||||
const fetchSettings = async () => {
|
||||
try {
|
||||
setLoading(true)
|
||||
const token = localStorage.getItem('token')
|
||||
const response = await axios.get(`${API_URL}/api/admin/config/by-category`, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
})
|
||||
|
||||
const configData = response.data
|
||||
const settingsObj: SecuritySettings = {
|
||||
enableRegistration: configData.features?.find((c: any) => c.key === 'enable_registration')?.value === 'true',
|
||||
enableEmailVerification: configData.features?.find((c: any) => c.key === 'enable_email_verification')?.value === 'true',
|
||||
enablePaymentVerification: configData.features?.find((c: any) => c.key === 'enable_payment_verification')?.value === 'true',
|
||||
}
|
||||
setSettings(settingsObj)
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch settings:', error)
|
||||
} finally {
|
||||
setLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
const handleSave = async () => {
|
||||
try {
|
||||
setSaving(true)
|
||||
const token = localStorage.getItem('token')
|
||||
|
||||
const configUpdates = [
|
||||
{ key: 'enable_registration', value: String(settings.enableRegistration), category: 'features', label: 'New User Registration', type: 'boolean' },
|
||||
{ key: 'enable_email_verification', value: String(settings.enableEmailVerification), category: 'features', label: 'Email Verification', type: 'boolean' },
|
||||
{ key: 'enable_payment_verification', value: String(settings.enablePaymentVerification), category: 'features', label: 'Payment Verification', type: 'boolean' },
|
||||
]
|
||||
|
||||
await Promise.all(
|
||||
configUpdates.map((config) =>
|
||||
axios.post(`${API_URL}/api/admin/config/${config.key}`, config, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
})
|
||||
)
|
||||
)
|
||||
|
||||
toast.success('Settings saved successfully')
|
||||
fetchSettings()
|
||||
} catch (error) {
|
||||
console.error('Failed to save settings:', error)
|
||||
toast.error('Failed to save settings')
|
||||
} finally {
|
||||
setSaving(false)
|
||||
}
|
||||
}
|
||||
|
||||
const handleChange = (field: keyof SecuritySettings, value: boolean) => {
|
||||
setSettings((prev) => ({ ...prev, [field]: value }))
|
||||
}
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="flex items-center justify-center h-64">
|
||||
<div className="text-muted-foreground">Loading...</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
{/* Feature Toggles Card */}
|
||||
<div className="bg-card rounded-xl border border-border p-6">
|
||||
<div className="flex items-center gap-3 mb-6">
|
||||
<div className="p-2 rounded-lg bg-primary/10">
|
||||
<Shield className="h-5 w-5 text-primary" />
|
||||
</div>
|
||||
<div>
|
||||
<h2 className="text-lg font-semibold text-foreground">Features & Security</h2>
|
||||
<p className="text-sm text-muted-foreground">Enable or disable features</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center justify-between p-4 rounded-lg bg-muted/50">
|
||||
<div>
|
||||
<p className="font-medium text-foreground">New User Registration</p>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Allow new users to register
|
||||
</p>
|
||||
</div>
|
||||
<Switch
|
||||
checked={settings.enableRegistration}
|
||||
onCheckedChange={(checked) => handleChange('enableRegistration', checked)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between p-4 rounded-lg bg-muted/50">
|
||||
<div>
|
||||
<p className="font-medium text-foreground">Email Verification</p>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Require email verification for new users
|
||||
</p>
|
||||
</div>
|
||||
<Switch
|
||||
checked={settings.enableEmailVerification}
|
||||
onCheckedChange={(checked) => handleChange('enableEmailVerification', checked)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between p-4 rounded-lg bg-muted/50">
|
||||
<div>
|
||||
<p className="font-medium text-foreground">Manual Payment Verification</p>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Enable manual verification for payments
|
||||
</p>
|
||||
</div>
|
||||
<Switch
|
||||
checked={settings.enablePaymentVerification}
|
||||
onCheckedChange={(checked) => handleChange('enablePaymentVerification', checked)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Save Button */}
|
||||
<div className="flex justify-end">
|
||||
<Button
|
||||
onClick={handleSave}
|
||||
disabled={saving}
|
||||
className="flex items-center gap-2"
|
||||
>
|
||||
<Save className="h-5 w-5" />
|
||||
{saving ? 'Saving...' : 'Save Settings'}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,32 +1,64 @@
|
||||
import { useEffect } from 'react'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useNavigate, useSearchParams } from 'react-router-dom'
|
||||
import { useAuth } from '@/contexts/AuthContext'
|
||||
import { Loader2 } from 'lucide-react'
|
||||
import axios from 'axios'
|
||||
|
||||
const API_URL = import.meta.env.VITE_API_URL || 'http://localhost:3001'
|
||||
|
||||
export function AuthCallback() {
|
||||
const navigate = useNavigate()
|
||||
const [searchParams] = useSearchParams()
|
||||
const { updateUser } = useAuth()
|
||||
const [error, setError] = useState('')
|
||||
|
||||
useEffect(() => {
|
||||
const token = searchParams.get('token')
|
||||
|
||||
if (token) {
|
||||
// Store token and redirect to dashboard
|
||||
localStorage.setItem('token', token)
|
||||
// Force reload to trigger auth context
|
||||
window.location.href = '/'
|
||||
} else {
|
||||
// No token, redirect to login
|
||||
navigate('/auth/login')
|
||||
const handleCallback = async () => {
|
||||
const token = searchParams.get('token')
|
||||
|
||||
if (!token) {
|
||||
navigate('/auth/login')
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
// Store token
|
||||
localStorage.setItem('token', token)
|
||||
|
||||
// Fetch user to check role
|
||||
const response = await axios.get(`${API_URL}/api/auth/me`, {
|
||||
headers: { Authorization: `Bearer ${token}` }
|
||||
})
|
||||
|
||||
// Redirect based on role
|
||||
if (response.data.role === 'admin') {
|
||||
window.location.href = '/admin'
|
||||
} else {
|
||||
window.location.href = '/'
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Failed to fetch user:', err)
|
||||
setError('Failed to complete sign in')
|
||||
localStorage.removeItem('token')
|
||||
setTimeout(() => navigate('/auth/login'), 2000)
|
||||
}
|
||||
}
|
||||
}, [searchParams, navigate, updateUser])
|
||||
|
||||
handleCallback()
|
||||
}, [searchParams, navigate])
|
||||
|
||||
return (
|
||||
<div className="min-h-screen flex items-center justify-center bg-gray-50">
|
||||
<div className="text-center">
|
||||
<Loader2 className="h-8 w-8 animate-spin mx-auto mb-4" />
|
||||
<p className="text-gray-600">Completing sign in...</p>
|
||||
{error ? (
|
||||
<>
|
||||
<p className="text-red-600 mb-4">{error}</p>
|
||||
<p className="text-gray-600">Redirecting to login...</p>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Loader2 className="h-8 w-8 animate-spin mx-auto mb-4" />
|
||||
<p className="text-gray-600">Completing sign in...</p>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
60
apps/web/src/components/pages/MaintenancePage.tsx
Normal file
60
apps/web/src/components/pages/MaintenancePage.tsx
Normal file
@@ -0,0 +1,60 @@
|
||||
import { AlertTriangle, RefreshCw } from 'lucide-react'
|
||||
import { Button } from '@/components/ui/button'
|
||||
|
||||
interface MaintenancePageProps {
|
||||
message?: string
|
||||
}
|
||||
|
||||
export function MaintenancePage({ message }: MaintenancePageProps) {
|
||||
const defaultMessage = 'System is under maintenance. Please try again later.'
|
||||
|
||||
const handleRefresh = () => {
|
||||
window.location.reload()
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="min-h-screen flex items-center justify-center bg-background p-4">
|
||||
<div className="max-w-md w-full text-center space-y-6">
|
||||
{/* Icon */}
|
||||
<div className="flex justify-center">
|
||||
<div className="p-6 rounded-full bg-yellow-500/10">
|
||||
<AlertTriangle className="h-16 w-16 text-yellow-600" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Title */}
|
||||
<div className="space-y-2">
|
||||
<h1 className="text-3xl font-bold text-foreground">
|
||||
Under Maintenance
|
||||
</h1>
|
||||
<p className="text-muted-foreground text-lg">
|
||||
{message || defaultMessage}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Description */}
|
||||
<div className="p-4 rounded-lg bg-muted/50 border border-border">
|
||||
<p className="text-sm text-muted-foreground">
|
||||
We're currently performing scheduled maintenance to improve your experience.
|
||||
We'll be back online shortly.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Refresh Button */}
|
||||
<Button
|
||||
onClick={handleRefresh}
|
||||
className="w-full"
|
||||
size="lg"
|
||||
>
|
||||
<RefreshCw className="h-5 w-5 mr-2" />
|
||||
Refresh Page
|
||||
</Button>
|
||||
|
||||
{/* Footer */}
|
||||
<p className="text-xs text-muted-foreground">
|
||||
Thank you for your patience
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -57,9 +57,13 @@ export function OtpVerification() {
|
||||
setLoading(true)
|
||||
|
||||
try {
|
||||
await verifyOtp(tempToken, code, method)
|
||||
// Verification successful, redirect to dashboard
|
||||
navigate('/')
|
||||
const result = await verifyOtp(tempToken, code, method)
|
||||
// Verification successful, redirect based on role
|
||||
if (result.user?.role === 'admin') {
|
||||
navigate('/admin')
|
||||
} else {
|
||||
navigate('/')
|
||||
}
|
||||
} catch (err) {
|
||||
const error = err as { response?: { data?: { message?: string } } }
|
||||
setError(error.response?.data?.message || 'Invalid OTP code. Please try again.')
|
||||
|
||||
@@ -498,14 +498,14 @@ export function Profile() {
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<div className="max-w-4xl mx-auto">
|
||||
<div className="mx-auto">
|
||||
<h1 className="text-3xl font-bold">{t.profile.title}</h1>
|
||||
<p className="text-muted-foreground">{t.profile.description}</p>
|
||||
</div>
|
||||
|
||||
<div className="max-w-4xl mx-auto">
|
||||
<div className="mx-auto">
|
||||
<Tabs defaultValue="profile" className="w-full">
|
||||
<TabsList className="grid w-[50%] grid-cols-2 h-auto p-1">
|
||||
<TabsList className="grid md:w-[40%] grid-cols-2 h-auto p-1">
|
||||
<TabsTrigger value="profile" className="h-11 md:h-9 text-base md:text-sm data-[state=active]:bg-background">
|
||||
{t.profile.editProfile}
|
||||
</TabsTrigger>
|
||||
@@ -516,6 +516,7 @@ export function Profile() {
|
||||
|
||||
{/* Edit Profile Tab */}
|
||||
<TabsContent value="profile" className="w-full space-y-6">
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-5">
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>{t.profile.personalInfo}</CardTitle>
|
||||
@@ -679,6 +680,7 @@ export function Profile() {
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</TabsContent>
|
||||
|
||||
{/* Security Tab */}
|
||||
@@ -720,7 +722,7 @@ export function Profile() {
|
||||
<Input
|
||||
id="current-password"
|
||||
type="password"
|
||||
placeholder="******"
|
||||
placeholder="••••••"
|
||||
value={currentPassword}
|
||||
onChange={(e) => setCurrentPassword(e.target.value)}
|
||||
disabled={passwordLoading}
|
||||
@@ -733,7 +735,7 @@ export function Profile() {
|
||||
<Input
|
||||
id="new-password"
|
||||
type="password"
|
||||
placeholder="******"
|
||||
placeholder="••••••"
|
||||
value={newPassword}
|
||||
onChange={(e) => setNewPassword(e.target.value)}
|
||||
disabled={passwordLoading}
|
||||
@@ -745,7 +747,7 @@ export function Profile() {
|
||||
<Input
|
||||
id="confirm-password"
|
||||
type="password"
|
||||
placeholder="******"
|
||||
placeholder="••••••"
|
||||
value={confirmPassword}
|
||||
onChange={(e) => setConfirmPassword(e.target.value)}
|
||||
disabled={passwordLoading}
|
||||
@@ -782,6 +784,8 @@ export function Profile() {
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-6">
|
||||
|
||||
<Separator />
|
||||
|
||||
{/* WhatsApp OTP */}
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center justify-between">
|
||||
@@ -794,7 +798,7 @@ export function Profile() {
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<Badge variant={otpStatus.whatsappEnabled ? "default" : "secondary"}>
|
||||
<Badge variant={otpStatus.whatsappEnabled ? "default" : "secondary"} className="text-nowrap">
|
||||
{otpStatus.whatsappEnabled ? t.profile.enabled : t.profile.disabled}
|
||||
</Badge>
|
||||
</div>
|
||||
@@ -895,7 +899,7 @@ export function Profile() {
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<Badge variant={otpStatus.emailEnabled ? "default" : "secondary"}>
|
||||
<Badge variant={otpStatus.emailEnabled ? "default" : "secondary"} className="text-nowrap">
|
||||
{otpStatus.emailEnabled ? t.profile.enabled : t.profile.disabled}
|
||||
</Badge>
|
||||
</div>
|
||||
@@ -974,7 +978,7 @@ export function Profile() {
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<Badge variant={otpStatus.totpEnabled ? "default" : "secondary"}>
|
||||
<Badge variant={otpStatus.totpEnabled ? "default" : "secondary"} className="text-nowrap">
|
||||
{otpStatus.totpEnabled ? t.profile.enabled : t.profile.disabled}
|
||||
</Badge>
|
||||
</div>
|
||||
|
||||
24
apps/web/src/utils/axiosSetup.ts
Normal file
24
apps/web/src/utils/axiosSetup.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import axios from 'axios'
|
||||
|
||||
let maintenanceCallback: ((message: string) => void) | null = null
|
||||
|
||||
export function setupAxiosInterceptors(onMaintenance: (message: string) => void) {
|
||||
maintenanceCallback = onMaintenance
|
||||
|
||||
// Response interceptor to handle maintenance mode
|
||||
axios.interceptors.response.use(
|
||||
(response) => response,
|
||||
(error) => {
|
||||
// Check if it's a maintenance mode error (503)
|
||||
if (error.response?.status === 503 && error.response?.data?.maintenanceMode) {
|
||||
const message = error.response.data.message || 'System is under maintenance. Please try again later.'
|
||||
if (maintenanceCallback) {
|
||||
maintenanceCallback(message)
|
||||
}
|
||||
// Prevent further error handling
|
||||
return Promise.reject({ maintenanceMode: true, message })
|
||||
}
|
||||
return Promise.reject(error)
|
||||
}
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user