@akanjs/cli 0.0.142 → 0.0.144
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -0
- package/cjs/index.js +2437 -3204
- package/cjs/src/guidelines/___library/sharedUiStructureDescription.en.md +786 -0
- package/cjs/src/guidelines/___library/utilUiStructureDescription.en.md +395 -0
- package/cjs/src/guidelines/___lint/lintRuleDescription.en.md +64 -0
- package/cjs/src/guidelines/___module/moduleStructureDescription.en.md +80 -0
- package/cjs/src/guidelines/componentRule/componentRule.instruction.md +732 -0
- package/cjs/src/guidelines/databaseModule/databaseModule.instruction.md +691 -0
- package/cjs/src/guidelines/enumConstant/enumConstant.instruction.md +232 -0
- package/cjs/src/guidelines/fieldDecorator/fieldDecorator.instruction.md +611 -0
- package/cjs/src/guidelines/framework/framework.instruction.md +1112 -0
- package/cjs/src/guidelines/modelConstant/modelConstant.instruction.md +958 -0
- package/cjs/src/guidelines/modelDictionary/modelDictionary.instruction.md +583 -0
- package/cjs/src/guidelines/modelDocument/modelDocument.instruction.md +683 -0
- package/cjs/src/guidelines/modelService/modelService.instruction.md +935 -0
- package/cjs/src/guidelines/modelSignal/modelSignal.instruction.md +588 -0
- package/cjs/src/guidelines/modelStore/modelStore.instruction.md +591 -0
- package/cjs/src/guidelines/modelTemplate/modelTemplate.instruction.md +577 -0
- package/cjs/src/guidelines/modelUnit/modelUnit.instruction.md +833 -0
- package/cjs/src/guidelines/modelUtil/modelUtil.instruction.md +752 -0
- package/cjs/src/guidelines/modelView/modelView.instruction.md +1005 -0
- package/cjs/src/guidelines/modelZone/modelZone.instruction.md +528 -0
- package/cjs/src/guidelines/scalarConstant/scalarConstant.instruction.md +489 -0
- package/cjs/src/guidelines/scalarDictionary/scalarDictionary.instruction.md +347 -0
- package/cjs/src/guidelines/sharedUiUsage/sharedUiUsage.instruction.md +318 -0
- package/cjs/src/guidelines/utilUiUsage/utilUiUsage.instruction.md +339 -0
- package/cjs/src/templates/module/__model__.dictionary.js +4 -5
- package/esm/index.js +2524 -3286
- package/esm/src/guidelines/___library/sharedUiStructureDescription.en.md +786 -0
- package/esm/src/guidelines/___library/utilUiStructureDescription.en.md +395 -0
- package/esm/src/guidelines/___lint/lintRuleDescription.en.md +64 -0
- package/esm/src/guidelines/___module/moduleStructureDescription.en.md +80 -0
- package/esm/src/guidelines/componentRule/componentRule.instruction.md +732 -0
- package/esm/src/guidelines/databaseModule/databaseModule.instruction.md +691 -0
- package/esm/src/guidelines/enumConstant/enumConstant.instruction.md +232 -0
- package/esm/src/guidelines/fieldDecorator/fieldDecorator.instruction.md +611 -0
- package/esm/src/guidelines/framework/framework.instruction.md +1112 -0
- package/esm/src/guidelines/modelConstant/modelConstant.instruction.md +958 -0
- package/esm/src/guidelines/modelDictionary/modelDictionary.instruction.md +583 -0
- package/esm/src/guidelines/modelDocument/modelDocument.instruction.md +683 -0
- package/esm/src/guidelines/modelService/modelService.instruction.md +935 -0
- package/esm/src/guidelines/modelSignal/modelSignal.instruction.md +588 -0
- package/esm/src/guidelines/modelStore/modelStore.instruction.md +591 -0
- package/esm/src/guidelines/modelTemplate/modelTemplate.instruction.md +577 -0
- package/esm/src/guidelines/modelUnit/modelUnit.instruction.md +833 -0
- package/esm/src/guidelines/modelUtil/modelUtil.instruction.md +752 -0
- package/esm/src/guidelines/modelView/modelView.instruction.md +1005 -0
- package/esm/src/guidelines/modelZone/modelZone.instruction.md +528 -0
- package/esm/src/guidelines/scalarConstant/scalarConstant.instruction.md +489 -0
- package/esm/src/guidelines/scalarDictionary/scalarDictionary.instruction.md +347 -0
- package/esm/src/guidelines/sharedUiUsage/sharedUiUsage.instruction.md +318 -0
- package/esm/src/guidelines/utilUiUsage/utilUiUsage.instruction.md +339 -0
- package/esm/src/templates/module/__model__.dictionary.js +4 -5
- package/package.json +3 -1
- package/src/guideline/guideline.command.d.ts +7 -0
- package/src/guideline/guideline.prompt.d.ts +15 -0
- package/src/guideline/guideline.runner.d.ts +5 -0
- package/src/guideline/guideline.script.d.ts +6 -0
- package/src/guidelines/___library/sharedUiStructureDescription.en.md +786 -0
- package/src/guidelines/___library/utilUiStructureDescription.en.md +395 -0
- package/src/guidelines/___lint/lintRuleDescription.en.md +64 -0
- package/src/guidelines/___module/moduleStructureDescription.en.md +80 -0
- package/src/guidelines/componentRule/componentRule.instruction.md +732 -0
- package/src/guidelines/databaseModule/databaseModule.instruction.md +691 -0
- package/src/guidelines/enumConstant/enumConstant.instruction.md +232 -0
- package/src/guidelines/fieldDecorator/fieldDecorator.instruction.md +611 -0
- package/src/guidelines/framework/framework.instruction.md +1112 -0
- package/src/guidelines/modelConstant/modelConstant.instruction.md +958 -0
- package/src/guidelines/modelDictionary/modelDictionary.instruction.md +583 -0
- package/src/guidelines/modelDocument/modelDocument.instruction.md +683 -0
- package/src/guidelines/modelService/modelService.instruction.md +935 -0
- package/src/guidelines/modelSignal/modelSignal.instruction.md +588 -0
- package/src/guidelines/modelStore/modelStore.instruction.md +591 -0
- package/src/guidelines/modelTemplate/modelTemplate.instruction.md +577 -0
- package/src/guidelines/modelUnit/modelUnit.instruction.md +833 -0
- package/src/guidelines/modelUtil/modelUtil.instruction.md +752 -0
- package/src/guidelines/modelView/modelView.instruction.md +1005 -0
- package/src/guidelines/modelZone/modelZone.instruction.md +528 -0
- package/src/guidelines/scalarConstant/scalarConstant.instruction.md +489 -0
- package/src/guidelines/scalarDictionary/scalarDictionary.instruction.md +347 -0
- package/src/guidelines/sharedUiUsage/sharedUiUsage.instruction.md +318 -0
- package/src/guidelines/utilUiUsage/utilUiUsage.instruction.md +339 -0
- package/src/module/module.command.d.ts +6 -8
- package/src/module/module.prompt.d.ts +2 -15
- package/src/module/module.request.d.ts +22 -18
- package/src/module/module.runner.d.ts +4 -20
- package/src/module/module.script.d.ts +6 -7
- package/src/scalar/scalar.command.d.ts +7 -0
- package/src/scalar/scalar.prompt.d.ts +23 -0
- package/src/scalar/scalar.runner.d.ts +13 -0
- package/src/scalar/scalar.script.d.ts +6 -0
- package/cjs/src/templates/app/app/[lang]/(__appName__)/(public)/forgotpassword/page.js +0 -47
- package/cjs/src/templates/app/app/[lang]/(__appName__)/(public)/page.js +0 -128
- package/cjs/src/templates/app/app/[lang]/(__appName__)/(public)/privacy/page.js +0 -42
- package/cjs/src/templates/app/app/[lang]/(__appName__)/(public)/signin/page.js +0 -50
- package/cjs/src/templates/app/app/[lang]/(__appName__)/(public)/termsofservice/page.js +0 -41
- package/cjs/src/templates/app/app/[lang]/(__appName__)/(public)/unknown/page.js +0 -51
- package/cjs/src/templates/app/app/[lang]/(__appName__)/(user)/layout.js +0 -43
- package/cjs/src/templates/app/app/[lang]/(__appName__)/(user)/self/page.js +0 -60
- package/cjs/src/templates/app/app/[lang]/(__appName__)/layout.js +0 -54
- package/cjs/src/templates/app/app/[lang]/(__appName__)/styles.css.template +0 -19
- package/cjs/src/templates/app/app/[lang]/admin/layout.js +0 -54
- package/cjs/src/templates/app/app/[lang]/admin/page.js +0 -63
- package/cjs/src/templates/app/app/csr.js +0 -34
- package/cjs/src/templates/app/app/index.html.template +0 -13
- package/cjs/src/templates/app/app/layout.js +0 -38
- package/cjs/src/templates/app/capacitor.config.ts.template +0 -8
- package/cjs/src/templates/app/env/env.client.debug.ts.template +0 -7
- package/cjs/src/templates/app/env/env.client.develop.ts.template +0 -7
- package/cjs/src/templates/app/env/env.client.local.ts.template +0 -7
- package/cjs/src/templates/app/env/env.client.main.ts.template +0 -7
- package/cjs/src/templates/app/env/env.client.testing.ts.template +0 -7
- package/cjs/src/templates/app/env/env.server.debug.ts.template +0 -15
- package/cjs/src/templates/app/env/env.server.develop.ts.template +0 -15
- package/cjs/src/templates/app/env/env.server.local.ts.template +0 -15
- package/cjs/src/templates/app/env/env.server.main.ts.template +0 -15
- package/cjs/src/templates/app/env/env.server.testing.ts.template +0 -7
- package/cjs/src/templates/app/lib/setting/Setting.Template.js +0 -57
- package/cjs/src/templates/app/lib/setting/Setting.Unit.js +0 -38
- package/cjs/src/templates/app/lib/setting/Setting.Util.js +0 -34
- package/cjs/src/templates/app/lib/setting/Setting.View.js +0 -51
- package/cjs/src/templates/app/lib/setting/Setting.Zone.js +0 -80
- package/cjs/src/templates/app/lib/setting/index.js +0 -61
- package/cjs/src/templates/app/lib/summary/Summary.Template.js +0 -43
- package/cjs/src/templates/app/lib/summary/Summary.Unit.js +0 -38
- package/cjs/src/templates/app/lib/summary/Summary.Util.js +0 -33
- package/cjs/src/templates/app/lib/summary/Summary.View.js +0 -51
- package/cjs/src/templates/app/lib/summary/Summary.Zone.js +0 -62
- package/cjs/src/templates/app/lib/summary/index.js +0 -67
- package/cjs/src/templates/app/lib/user/User.Template.js +0 -65
- package/cjs/src/templates/app/lib/user/User.Unit.js +0 -38
- package/cjs/src/templates/app/lib/user/User.Util.js +0 -94
- package/cjs/src/templates/app/lib/user/User.View.js +0 -66
- package/cjs/src/templates/app/lib/user/User.Zone.js +0 -74
- package/cjs/src/templates/app/lib/user/index.js +0 -61
- package/cjs/src/templates/app/page.test.ts.template +0 -10
- package/cjs/src/templates/app/playwright.config.ts.template +0 -6
- package/cjs/src/templates/app/postcss.config.js.template +0 -10
- package/cjs/src/templates/app/public/manifest.json.template +0 -67
- package/cjs/src/templates/app/tsconfig.json.template +0 -22
- package/cjs/src/templates/app/tsconfig.spec.json.template +0 -7
- package/cjs/src/templates/app/ui/Footer.js +0 -67
- package/cjs/src/templates/app/ui/MainHeader.js +0 -131
- package/cjs/src/templates/crudPages/[__model__Id]/edit/page.js +0 -73
- package/cjs/src/templates/crudPages/[__model__Id]/page.js +0 -83
- package/cjs/src/templates/crudPages/new/page.js +0 -70
- package/cjs/src/templates/crudPages/page.js +0 -71
- package/cjs/src/templates/libRoot/.gitignore.template +0 -15
- package/cjs/src/templates/libRoot/env/env.server.example.ts.template +0 -7
- package/cjs/src/templates/libRoot/env/env.server.testing.ts.template +0 -7
- package/cjs/src/templates/libRoot/lib/setting/Setting.Template.js +0 -57
- package/cjs/src/templates/libRoot/lib/setting/Setting.Unit.js +0 -38
- package/cjs/src/templates/libRoot/lib/setting/Setting.Util.js +0 -34
- package/cjs/src/templates/libRoot/lib/setting/Setting.View.js +0 -51
- package/cjs/src/templates/libRoot/lib/setting/Setting.Zone.js +0 -80
- package/cjs/src/templates/libRoot/lib/setting/index.js +0 -61
- package/cjs/src/templates/libRoot/lib/summary/Summary.Template.js +0 -43
- package/cjs/src/templates/libRoot/lib/summary/Summary.Unit.js +0 -38
- package/cjs/src/templates/libRoot/lib/summary/Summary.Util.js +0 -33
- package/cjs/src/templates/libRoot/lib/summary/Summary.View.js +0 -51
- package/cjs/src/templates/libRoot/lib/summary/Summary.Zone.js +0 -62
- package/cjs/src/templates/libRoot/lib/summary/index.js +0 -67
- package/cjs/src/templates/libRoot/lib/user/User.Template.js +0 -65
- package/cjs/src/templates/libRoot/lib/user/User.Unit.js +0 -38
- package/cjs/src/templates/libRoot/lib/user/User.Util.js +0 -94
- package/cjs/src/templates/libRoot/lib/user/User.View.js +0 -66
- package/cjs/src/templates/libRoot/lib/user/User.Zone.js +0 -74
- package/cjs/src/templates/libRoot/lib/user/index.js +0 -61
- package/cjs/src/templates/libRoot/package.json.template +0 -4
- package/cjs/src/templates/libRoot/tsconfig.json.template +0 -13
- package/cjs/src/templates/libRoot/tsconfig.spec.json.template +0 -7
- package/cjs/src/templates/localDev/docker-compose.yaml.template +0 -36
- package/cjs/src/templates/module/__Model__.Template.js +0 -54
- package/cjs/src/templates/module/__Model__.Unit.js +0 -42
- package/cjs/src/templates/module/__Model__.Util.js +0 -70
- package/cjs/src/templates/module/__Model__.View.js +0 -48
- package/cjs/src/templates/module/__Model__.Zone.js +0 -83
- package/cjs/src/templates/module/index.js +0 -61
- package/cjs/src/templates/pkgRoot/tsconfig.json.template +0 -15
- package/cjs/src/templates/workspaceRoot/.env.template +0 -20
- package/cjs/src/templates/workspaceRoot/.gitignore.template +0 -118
- package/cjs/src/templates/workspaceRoot/.prettierignore.template +0 -10
- package/cjs/src/templates/workspaceRoot/.prettierrc.json.template +0 -6
- package/cjs/src/templates/workspaceRoot/.swcrc.template +0 -9
- package/cjs/src/templates/workspaceRoot/.vscode/settings.json.template +0 -13
- package/cjs/src/templates/workspaceRoot/README.md.template +0 -37
- package/cjs/src/templates/workspaceRoot/eslint.config.ts.template +0 -3
- package/cjs/src/templates/workspaceRoot/package.json.template +0 -43
- package/cjs/src/templates/workspaceRoot/tsconfig.json.template +0 -29
- package/esm/src/templates/app/app/[lang]/(__appName__)/(public)/forgotpassword/page.js +0 -27
- package/esm/src/templates/app/app/[lang]/(__appName__)/(public)/page.js +0 -108
- package/esm/src/templates/app/app/[lang]/(__appName__)/(public)/privacy/page.js +0 -22
- package/esm/src/templates/app/app/[lang]/(__appName__)/(public)/signin/page.js +0 -30
- package/esm/src/templates/app/app/[lang]/(__appName__)/(public)/termsofservice/page.js +0 -21
- package/esm/src/templates/app/app/[lang]/(__appName__)/(public)/unknown/page.js +0 -31
- package/esm/src/templates/app/app/[lang]/(__appName__)/(user)/layout.js +0 -23
- package/esm/src/templates/app/app/[lang]/(__appName__)/(user)/self/page.js +0 -40
- package/esm/src/templates/app/app/[lang]/(__appName__)/layout.js +0 -34
- package/esm/src/templates/app/app/[lang]/(__appName__)/styles.css.template +0 -19
- package/esm/src/templates/app/app/[lang]/admin/layout.js +0 -34
- package/esm/src/templates/app/app/[lang]/admin/page.js +0 -43
- package/esm/src/templates/app/app/csr.js +0 -14
- package/esm/src/templates/app/app/index.html.template +0 -13
- package/esm/src/templates/app/app/layout.js +0 -18
- package/esm/src/templates/app/capacitor.config.ts.template +0 -8
- package/esm/src/templates/app/env/env.client.debug.ts.template +0 -7
- package/esm/src/templates/app/env/env.client.develop.ts.template +0 -7
- package/esm/src/templates/app/env/env.client.local.ts.template +0 -7
- package/esm/src/templates/app/env/env.client.main.ts.template +0 -7
- package/esm/src/templates/app/env/env.client.testing.ts.template +0 -7
- package/esm/src/templates/app/env/env.server.debug.ts.template +0 -15
- package/esm/src/templates/app/env/env.server.develop.ts.template +0 -15
- package/esm/src/templates/app/env/env.server.local.ts.template +0 -15
- package/esm/src/templates/app/env/env.server.main.ts.template +0 -15
- package/esm/src/templates/app/env/env.server.testing.ts.template +0 -7
- package/esm/src/templates/app/lib/setting/Setting.Template.js +0 -37
- package/esm/src/templates/app/lib/setting/Setting.Unit.js +0 -18
- package/esm/src/templates/app/lib/setting/Setting.Util.js +0 -14
- package/esm/src/templates/app/lib/setting/Setting.View.js +0 -31
- package/esm/src/templates/app/lib/setting/Setting.Zone.js +0 -60
- package/esm/src/templates/app/lib/setting/index.js +0 -41
- package/esm/src/templates/app/lib/summary/Summary.Template.js +0 -23
- package/esm/src/templates/app/lib/summary/Summary.Unit.js +0 -18
- package/esm/src/templates/app/lib/summary/Summary.Util.js +0 -13
- package/esm/src/templates/app/lib/summary/Summary.View.js +0 -31
- package/esm/src/templates/app/lib/summary/Summary.Zone.js +0 -42
- package/esm/src/templates/app/lib/summary/index.js +0 -47
- package/esm/src/templates/app/lib/user/User.Template.js +0 -45
- package/esm/src/templates/app/lib/user/User.Unit.js +0 -18
- package/esm/src/templates/app/lib/user/User.Util.js +0 -74
- package/esm/src/templates/app/lib/user/User.View.js +0 -46
- package/esm/src/templates/app/lib/user/User.Zone.js +0 -54
- package/esm/src/templates/app/lib/user/index.js +0 -41
- package/esm/src/templates/app/page.test.ts.template +0 -10
- package/esm/src/templates/app/playwright.config.ts.template +0 -6
- package/esm/src/templates/app/postcss.config.js.template +0 -10
- package/esm/src/templates/app/public/manifest.json.template +0 -67
- package/esm/src/templates/app/tsconfig.json.template +0 -22
- package/esm/src/templates/app/tsconfig.spec.json.template +0 -7
- package/esm/src/templates/app/ui/Footer.js +0 -47
- package/esm/src/templates/app/ui/MainHeader.js +0 -111
- package/esm/src/templates/crudPages/[__model__Id]/edit/page.js +0 -53
- package/esm/src/templates/crudPages/[__model__Id]/page.js +0 -63
- package/esm/src/templates/crudPages/new/page.js +0 -50
- package/esm/src/templates/crudPages/page.js +0 -51
- package/esm/src/templates/libRoot/.gitignore.template +0 -15
- package/esm/src/templates/libRoot/env/env.server.example.ts.template +0 -7
- package/esm/src/templates/libRoot/env/env.server.testing.ts.template +0 -7
- package/esm/src/templates/libRoot/lib/setting/Setting.Template.js +0 -37
- package/esm/src/templates/libRoot/lib/setting/Setting.Unit.js +0 -18
- package/esm/src/templates/libRoot/lib/setting/Setting.Util.js +0 -14
- package/esm/src/templates/libRoot/lib/setting/Setting.View.js +0 -31
- package/esm/src/templates/libRoot/lib/setting/Setting.Zone.js +0 -60
- package/esm/src/templates/libRoot/lib/setting/index.js +0 -41
- package/esm/src/templates/libRoot/lib/summary/Summary.Template.js +0 -23
- package/esm/src/templates/libRoot/lib/summary/Summary.Unit.js +0 -18
- package/esm/src/templates/libRoot/lib/summary/Summary.Util.js +0 -13
- package/esm/src/templates/libRoot/lib/summary/Summary.View.js +0 -31
- package/esm/src/templates/libRoot/lib/summary/Summary.Zone.js +0 -42
- package/esm/src/templates/libRoot/lib/summary/index.js +0 -47
- package/esm/src/templates/libRoot/lib/user/User.Template.js +0 -45
- package/esm/src/templates/libRoot/lib/user/User.Unit.js +0 -18
- package/esm/src/templates/libRoot/lib/user/User.Util.js +0 -74
- package/esm/src/templates/libRoot/lib/user/User.View.js +0 -46
- package/esm/src/templates/libRoot/lib/user/User.Zone.js +0 -54
- package/esm/src/templates/libRoot/lib/user/index.js +0 -41
- package/esm/src/templates/libRoot/package.json.template +0 -4
- package/esm/src/templates/libRoot/tsconfig.json.template +0 -13
- package/esm/src/templates/libRoot/tsconfig.spec.json.template +0 -7
- package/esm/src/templates/localDev/docker-compose.yaml.template +0 -36
- package/esm/src/templates/module/__Model__.Template.js +0 -34
- package/esm/src/templates/module/__Model__.Unit.js +0 -22
- package/esm/src/templates/module/__Model__.Util.js +0 -50
- package/esm/src/templates/module/__Model__.View.js +0 -28
- package/esm/src/templates/module/__Model__.Zone.js +0 -63
- package/esm/src/templates/module/index.js +0 -41
- package/esm/src/templates/pkgRoot/tsconfig.json.template +0 -15
- package/esm/src/templates/workspaceRoot/.env.template +0 -20
- package/esm/src/templates/workspaceRoot/.gitignore.template +0 -118
- package/esm/src/templates/workspaceRoot/.prettierignore.template +0 -10
- package/esm/src/templates/workspaceRoot/.prettierrc.json.template +0 -6
- package/esm/src/templates/workspaceRoot/.swcrc.template +0 -9
- package/esm/src/templates/workspaceRoot/.vscode/settings.json.template +0 -13
- package/esm/src/templates/workspaceRoot/README.md.template +0 -37
- package/esm/src/templates/workspaceRoot/eslint.config.ts.template +0 -3
- package/esm/src/templates/workspaceRoot/package.json.template +0 -43
- package/esm/src/templates/workspaceRoot/tsconfig.json.template +0 -29
- package/src/application/application.prompt.d.ts +0 -2
- package/src/templates/app/app/[lang]/(__appName__)/(public)/forgotpassword/page.d.ts +0 -9
- package/src/templates/app/app/[lang]/(__appName__)/(public)/page.d.ts +0 -9
- package/src/templates/app/app/[lang]/(__appName__)/(public)/privacy/page.d.ts +0 -9
- package/src/templates/app/app/[lang]/(__appName__)/(public)/signin/page.d.ts +0 -9
- package/src/templates/app/app/[lang]/(__appName__)/(public)/termsofservice/page.d.ts +0 -10
- package/src/templates/app/app/[lang]/(__appName__)/(public)/unknown/page.d.ts +0 -9
- package/src/templates/app/app/[lang]/(__appName__)/(user)/layout.d.ts +0 -9
- package/src/templates/app/app/[lang]/(__appName__)/(user)/self/page.d.ts +0 -9
- package/src/templates/app/app/[lang]/(__appName__)/layout.d.ts +0 -9
- package/src/templates/app/app/[lang]/admin/layout.d.ts +0 -9
- package/src/templates/app/app/[lang]/admin/page.d.ts +0 -9
- package/src/templates/app/app/csr.d.ts +0 -9
- package/src/templates/app/app/layout.d.ts +0 -9
- package/src/templates/app/lib/setting/Setting.Template.d.ts +0 -9
- package/src/templates/app/lib/setting/Setting.Unit.d.ts +0 -9
- package/src/templates/app/lib/setting/Setting.Util.d.ts +0 -9
- package/src/templates/app/lib/setting/Setting.View.d.ts +0 -9
- package/src/templates/app/lib/setting/Setting.Zone.d.ts +0 -9
- package/src/templates/app/lib/setting/index.d.ts +0 -9
- package/src/templates/app/lib/summary/Summary.Template.d.ts +0 -9
- package/src/templates/app/lib/summary/Summary.Unit.d.ts +0 -9
- package/src/templates/app/lib/summary/Summary.Util.d.ts +0 -9
- package/src/templates/app/lib/summary/Summary.View.d.ts +0 -9
- package/src/templates/app/lib/summary/Summary.Zone.d.ts +0 -9
- package/src/templates/app/lib/summary/index.d.ts +0 -9
- package/src/templates/app/lib/user/User.Template.d.ts +0 -9
- package/src/templates/app/lib/user/User.Unit.d.ts +0 -9
- package/src/templates/app/lib/user/User.Util.d.ts +0 -9
- package/src/templates/app/lib/user/User.View.d.ts +0 -9
- package/src/templates/app/lib/user/User.Zone.d.ts +0 -9
- package/src/templates/app/lib/user/index.d.ts +0 -9
- package/src/templates/app/ui/Footer.d.ts +0 -9
- package/src/templates/app/ui/MainHeader.d.ts +0 -10
- package/src/templates/crudPages/[__model__Id]/edit/page.d.ts +0 -11
- package/src/templates/crudPages/[__model__Id]/page.d.ts +0 -11
- package/src/templates/crudPages/new/page.d.ts +0 -11
- package/src/templates/crudPages/page.d.ts +0 -11
- package/src/templates/libRoot/lib/setting/Setting.Template.d.ts +0 -9
- package/src/templates/libRoot/lib/setting/Setting.Unit.d.ts +0 -9
- package/src/templates/libRoot/lib/setting/Setting.Util.d.ts +0 -9
- package/src/templates/libRoot/lib/setting/Setting.View.d.ts +0 -9
- package/src/templates/libRoot/lib/setting/Setting.Zone.d.ts +0 -9
- package/src/templates/libRoot/lib/setting/index.d.ts +0 -9
- package/src/templates/libRoot/lib/summary/Summary.Template.d.ts +0 -9
- package/src/templates/libRoot/lib/summary/Summary.Unit.d.ts +0 -9
- package/src/templates/libRoot/lib/summary/Summary.Util.d.ts +0 -9
- package/src/templates/libRoot/lib/summary/Summary.View.d.ts +0 -9
- package/src/templates/libRoot/lib/summary/Summary.Zone.d.ts +0 -9
- package/src/templates/libRoot/lib/summary/index.d.ts +0 -9
- package/src/templates/libRoot/lib/user/User.Template.d.ts +0 -9
- package/src/templates/libRoot/lib/user/User.Unit.d.ts +0 -9
- package/src/templates/libRoot/lib/user/User.Util.d.ts +0 -9
- package/src/templates/libRoot/lib/user/User.View.d.ts +0 -9
- package/src/templates/libRoot/lib/user/User.Zone.d.ts +0 -9
- package/src/templates/libRoot/lib/user/index.d.ts +0 -9
- package/src/templates/module/__Model__.Template.d.ts +0 -11
- package/src/templates/module/__Model__.Unit.d.ts +0 -11
- package/src/templates/module/__Model__.Util.d.ts +0 -11
- package/src/templates/module/__Model__.View.d.ts +0 -11
- package/src/templates/module/__Model__.Zone.d.ts +0 -11
- package/src/templates/module/index.d.ts +0 -11
|
@@ -0,0 +1,1005 @@
|
|
|
1
|
+
# Akan.js Model.View.tsx Implementation Guide
|
|
2
|
+
|
|
3
|
+
## Purpose and Role of Model.View.tsx Files in the Akan.js Architecture
|
|
4
|
+
|
|
5
|
+
Model.View.tsx files serve as specialized server components within the Akan.js framework that provide comprehensive presentation layers for domain models. They fulfill several critical roles:
|
|
6
|
+
|
|
7
|
+
- **Complete Data Visualization**: Present full, detailed views of domain entities
|
|
8
|
+
- **Page-Level Components**: Serve as primary content components for application pages
|
|
9
|
+
- **Consistent Representation**: Ensure uniform presentation of domain models
|
|
10
|
+
- **Server-Side Optimization**: Leverage Next.js server components for performance
|
|
11
|
+
- **Cross-Component Integration**: Provide view components that integrate with the broader component ecosystem
|
|
12
|
+
|
|
13
|
+
In the Akan.js component hierarchy, View components sit between high-level Zones (layout containers) and lower-level Units (list items/cards), providing comprehensive representations of domain models.
|
|
14
|
+
|
|
15
|
+
## Core Principles of Model.View.tsx Components
|
|
16
|
+
|
|
17
|
+
1. **Server-First Architecture**: View components are server components, optimized for static rendering
|
|
18
|
+
2. **Presentation Focus**: Concentrate solely on displaying data, not managing state or handling interactions
|
|
19
|
+
3. **Model-Centric Design**: Build around domain model structures (`cnst.Model`)
|
|
20
|
+
4. **Composition-Based**: Composed of smaller, focused components for different aspects of the model
|
|
21
|
+
5. **Responsive UI**: Adapt to different screen sizes and viewport conditions
|
|
22
|
+
6. **Accessibility**: Follow best practices for screen readers and keyboard navigation
|
|
23
|
+
7. **No Client-Side Hooks**: Avoid `useState`, `useEffect`, and other client-side React hooks
|
|
24
|
+
|
|
25
|
+
## File Structure and Component Organization
|
|
26
|
+
|
|
27
|
+
### Location and Naming Convention
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
apps/
|
|
31
|
+
app-name/
|
|
32
|
+
lib/
|
|
33
|
+
feature-name/
|
|
34
|
+
FeatureName.View.tsx # PascalCase for file and component names
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### Component Structure
|
|
38
|
+
|
|
39
|
+
A typical Model.View.tsx file exports multiple component variations:
|
|
40
|
+
|
|
41
|
+
```tsx
|
|
42
|
+
import { clsx } from "clsx";
|
|
43
|
+
import { cnst } from "@your-lib/client";
|
|
44
|
+
import { Card, Section } from "@util/ui";
|
|
45
|
+
|
|
46
|
+
// Basic view interface
|
|
47
|
+
export interface ProductViewProps {
|
|
48
|
+
product: cnst.Product;
|
|
49
|
+
className?: string;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// General view (default/standard view)
|
|
53
|
+
export const General = ({ product, className }: ProductViewProps) => (
|
|
54
|
+
<div className={clsx("animate-fadeIn space-y-6", className)}>
|
|
55
|
+
<h1 className="text-2xl font-bold">{product.name}</h1>
|
|
56
|
+
<div className="flex gap-4">
|
|
57
|
+
<div className="w-2/3">
|
|
58
|
+
<h2 className="mb-2 text-xl">Description</h2>
|
|
59
|
+
<p>{product.description}</p>
|
|
60
|
+
{/* More product details */}
|
|
61
|
+
</div>
|
|
62
|
+
<div className="w-1/3">
|
|
63
|
+
<Card className="p-4">
|
|
64
|
+
<h3 className="mb-2 text-lg font-medium">Summary</h3>
|
|
65
|
+
<div className="space-y-2">
|
|
66
|
+
<div className="flex justify-between">
|
|
67
|
+
<span>Price:</span>
|
|
68
|
+
<span className="font-bold">{product.price.toLocaleString()} KRW</span>
|
|
69
|
+
</div>
|
|
70
|
+
<div className="flex justify-between">
|
|
71
|
+
<span>In Stock:</span>
|
|
72
|
+
<span>{product.inStock ? "Yes" : "No"}</span>
|
|
73
|
+
</div>
|
|
74
|
+
</div>
|
|
75
|
+
</Card>
|
|
76
|
+
</div>
|
|
77
|
+
</div>
|
|
78
|
+
</div>
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
// Specialized views for different contexts
|
|
82
|
+
export const Dashboard = ({ product }: ProductViewProps) => (
|
|
83
|
+
<Section title={product.name} className="dashboard-view">
|
|
84
|
+
{/* Dashboard-specific presentation */}
|
|
85
|
+
</Section>
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
export const Printable = ({ product }: ProductViewProps) => (
|
|
89
|
+
<div className="print-only">{/* Print-optimized view */}</div>
|
|
90
|
+
);
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Props Design and Interfaces
|
|
94
|
+
|
|
95
|
+
Model.View components use consistent prop interfaces that follow these patterns:
|
|
96
|
+
|
|
97
|
+
```tsx
|
|
98
|
+
// Basic pattern
|
|
99
|
+
export interface ProductViewProps {
|
|
100
|
+
product: cnst.Product; // The primary model object
|
|
101
|
+
className?: string; // For styling customization
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// With additional configuration options
|
|
105
|
+
export interface OrderViewProps {
|
|
106
|
+
order: cnst.Order;
|
|
107
|
+
className?: string;
|
|
108
|
+
showDetails?: boolean; // Optional display configuration
|
|
109
|
+
highlightStatus?: boolean;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// With variant/type discrimination
|
|
113
|
+
export type InvoiceViewProps =
|
|
114
|
+
| { invoice: cnst.Invoice; variant: "summary"; className?: string }
|
|
115
|
+
| { invoice: cnst.Invoice; variant: "detailed"; showPayments: boolean; className?: string };
|
|
116
|
+
|
|
117
|
+
export const Invoice = (props: InvoiceViewProps) => {
|
|
118
|
+
if (props.variant === "summary") {
|
|
119
|
+
return <InvoiceSummary invoice={props.invoice} className={props.className} />;
|
|
120
|
+
} else {
|
|
121
|
+
return <InvoiceDetailed invoice={props.invoice} showPayments={props.showPayments} className={props.className} />;
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## Component Variations and Specialized Views
|
|
127
|
+
|
|
128
|
+
Model.View components typically include multiple variations for different use cases:
|
|
129
|
+
|
|
130
|
+
1. **General**: Standard, complete view of the model
|
|
131
|
+
2. **Summary/Dashboard**: Condensed view with key information
|
|
132
|
+
3. **Detailed**: Expanded view with all available information
|
|
133
|
+
4. **Print/Export**: View optimized for printing or exporting
|
|
134
|
+
5. **Specialized**: Context-specific views (admin, customer, etc.)
|
|
135
|
+
|
|
136
|
+
Example implementation:
|
|
137
|
+
|
|
138
|
+
```tsx
|
|
139
|
+
// General view (standard)
|
|
140
|
+
export const General = ({ project }: ProjectViewProps) => (
|
|
141
|
+
<div className="project-view">
|
|
142
|
+
<h1>{project.name}</h1>
|
|
143
|
+
<div className="grid grid-cols-1 gap-6 md:grid-cols-2">{/* Project information organized in a grid */}</div>
|
|
144
|
+
</div>
|
|
145
|
+
);
|
|
146
|
+
|
|
147
|
+
// Timeline view (specialized)
|
|
148
|
+
export const Timeline = ({ project }: ProjectViewProps) => (
|
|
149
|
+
<div className="project-timeline">{/* Timeline visualization of project milestones */}</div>
|
|
150
|
+
);
|
|
151
|
+
|
|
152
|
+
// Financial view (specialized)
|
|
153
|
+
export const Financial = ({ project }: ProjectViewProps) => (
|
|
154
|
+
<div className="project-financials">{/* Financial breakdown and charts */}</div>
|
|
155
|
+
);
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
## Integration with Other Akan.js Components
|
|
159
|
+
|
|
160
|
+
Model.View components work seamlessly with other components in the Akan.js ecosystem:
|
|
161
|
+
|
|
162
|
+
### Used in Pages
|
|
163
|
+
|
|
164
|
+
```tsx
|
|
165
|
+
// app/products/[id]/page.tsx
|
|
166
|
+
import { Product } from "@shared/client";
|
|
167
|
+
|
|
168
|
+
export default async function ProductPage({ params }: { params: { id: string } }) {
|
|
169
|
+
const product = await getProductById(params.id);
|
|
170
|
+
|
|
171
|
+
return (
|
|
172
|
+
<div className="container mx-auto py-8">
|
|
173
|
+
<Product.View.General product={product} />
|
|
174
|
+
</div>
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### Used in Utils (Client Components)
|
|
180
|
+
|
|
181
|
+
```tsx
|
|
182
|
+
// Product.Util.tsx
|
|
183
|
+
"use client";
|
|
184
|
+
import { useState } from "react";
|
|
185
|
+
import { Product } from "@shared/client";
|
|
186
|
+
import { cnst, st } from "@shared/client";
|
|
187
|
+
|
|
188
|
+
export const ProductViewer = ({ productId }: { productId: string }) => {
|
|
189
|
+
const product = st.use.product(productId);
|
|
190
|
+
const [view, setView] = useState<"general" | "specifications">("general");
|
|
191
|
+
|
|
192
|
+
if (!product) return <div>Loading...</div>;
|
|
193
|
+
|
|
194
|
+
return (
|
|
195
|
+
<div>
|
|
196
|
+
<div className="tabs mb-4">
|
|
197
|
+
<button className={`tab ${view === "general" ? "tab-active" : ""}`} onClick={() => setView("general")}>
|
|
198
|
+
General
|
|
199
|
+
</button>
|
|
200
|
+
<button
|
|
201
|
+
className={`tab ${view === "specifications" ? "tab-active" : ""}`}
|
|
202
|
+
onClick={() => setView("specifications")}
|
|
203
|
+
>
|
|
204
|
+
Specifications
|
|
205
|
+
</button>
|
|
206
|
+
</div>
|
|
207
|
+
|
|
208
|
+
{view === "general" ? (
|
|
209
|
+
<Product.View.General product={product} />
|
|
210
|
+
) : (
|
|
211
|
+
<Product.View.Specifications product={product} />
|
|
212
|
+
)}
|
|
213
|
+
</div>
|
|
214
|
+
);
|
|
215
|
+
};
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
### Used in Zones (Layout Containers)
|
|
219
|
+
|
|
220
|
+
```tsx
|
|
221
|
+
// Product.Zone.tsx
|
|
222
|
+
"use client";
|
|
223
|
+
import { useState } from "react";
|
|
224
|
+
import { Product } from "@shared/client";
|
|
225
|
+
import { cnst, st } from "@shared/client";
|
|
226
|
+
import { Zone } from "@util/ui";
|
|
227
|
+
|
|
228
|
+
export const ProductManagerZone = () => {
|
|
229
|
+
const products = st.use.productList();
|
|
230
|
+
const [selectedProduct, setSelectedProduct] = useState<cnst.Product | null>(null);
|
|
231
|
+
|
|
232
|
+
return (
|
|
233
|
+
<Zone title="Product Management">
|
|
234
|
+
<div className="grid grid-cols-1 gap-6 lg:grid-cols-3">
|
|
235
|
+
<div className="lg:col-span-1">
|
|
236
|
+
<Zone.Section title="Products">
|
|
237
|
+
{/* Product list for selection */}
|
|
238
|
+
{products.map((product) => (
|
|
239
|
+
<button key={product.id} onClick={() => setSelectedProduct(product)} className="mb-2 w-full text-left">
|
|
240
|
+
<Product.Unit.Abstract product={product} />
|
|
241
|
+
</button>
|
|
242
|
+
))}
|
|
243
|
+
</Zone.Section>
|
|
244
|
+
</div>
|
|
245
|
+
|
|
246
|
+
<div className="lg:col-span-2">
|
|
247
|
+
<Zone.Section title="Selected Product">
|
|
248
|
+
{selectedProduct ? (
|
|
249
|
+
<Product.View.General product={selectedProduct} />
|
|
250
|
+
) : (
|
|
251
|
+
<div className="py-8 text-center text-gray-500">Select a product to view details</div>
|
|
252
|
+
)}
|
|
253
|
+
</Zone.Section>
|
|
254
|
+
</div>
|
|
255
|
+
</div>
|
|
256
|
+
</Zone>
|
|
257
|
+
);
|
|
258
|
+
};
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
## Server Component Considerations
|
|
262
|
+
|
|
263
|
+
Model.View.tsx files are server components and must follow these constraints:
|
|
264
|
+
|
|
265
|
+
1. **No Client-Side Hooks**:
|
|
266
|
+
|
|
267
|
+
- No `useState`, `useEffect`, `useRef`, etc.
|
|
268
|
+
- No `useRouter` or other Next.js client hooks
|
|
269
|
+
- No browser APIs (`window`, `localStorage`, etc.)
|
|
270
|
+
|
|
271
|
+
2. **No Event Handlers**:
|
|
272
|
+
|
|
273
|
+
- No `onClick`, `onChange`, or other event handlers
|
|
274
|
+
- No form submission handling
|
|
275
|
+
|
|
276
|
+
3. **No "use client" Directive**:
|
|
277
|
+
|
|
278
|
+
- The file should not include the "use client" directive
|
|
279
|
+
|
|
280
|
+
4. **Static Props Only**:
|
|
281
|
+
- Props should be static and not depend on client-side state
|
|
282
|
+
- Data should be passed in explicitly through props
|
|
283
|
+
|
|
284
|
+
If you need interactivity, wrap the View component in a client component (typically in a Util.tsx file).
|
|
285
|
+
|
|
286
|
+
## Best Practices for Effective View Components
|
|
287
|
+
|
|
288
|
+
### 1. Consistent Layout Structure
|
|
289
|
+
|
|
290
|
+
```tsx
|
|
291
|
+
export const General = ({ invoice }: InvoiceViewProps) => (
|
|
292
|
+
<div className="space-y-8">
|
|
293
|
+
{/* Header section */}
|
|
294
|
+
<div className="flex items-center justify-between">
|
|
295
|
+
<h1 className="text-2xl font-bold">Invoice #{invoice.number}</h1>
|
|
296
|
+
<div className="badge badge-lg">{invoice.status}</div>
|
|
297
|
+
</div>
|
|
298
|
+
|
|
299
|
+
{/* Metadata section */}
|
|
300
|
+
<div className="grid grid-cols-2 gap-4 rounded-lg bg-gray-50 p-4">
|
|
301
|
+
<div>
|
|
302
|
+
<h2 className="text-sm text-gray-500">Client</h2>
|
|
303
|
+
<p className="font-medium">{invoice.client.name}</p>
|
|
304
|
+
</div>
|
|
305
|
+
<div>
|
|
306
|
+
<h2 className="text-sm text-gray-500">Issue Date</h2>
|
|
307
|
+
<p className="font-medium">{formatDate(invoice.issueDate)}</p>
|
|
308
|
+
</div>
|
|
309
|
+
{/* More metadata fields */}
|
|
310
|
+
</div>
|
|
311
|
+
|
|
312
|
+
{/* Main content section */}
|
|
313
|
+
<div>
|
|
314
|
+
<h2 className="mb-4 text-xl font-semibold">Line Items</h2>
|
|
315
|
+
<table className="w-full">{/* Table content */}</table>
|
|
316
|
+
</div>
|
|
317
|
+
|
|
318
|
+
{/* Summary section */}
|
|
319
|
+
<div className="flex justify-end">
|
|
320
|
+
<div className="w-64 space-y-2">
|
|
321
|
+
<div className="flex justify-between">
|
|
322
|
+
<span>Subtotal:</span>
|
|
323
|
+
<span>{formatCurrency(invoice.subtotal)}</span>
|
|
324
|
+
</div>
|
|
325
|
+
{/* More summary rows */}
|
|
326
|
+
<div className="flex justify-between border-t pt-2 text-lg font-bold">
|
|
327
|
+
<span>Total:</span>
|
|
328
|
+
<span>{formatCurrency(invoice.total)}</span>
|
|
329
|
+
</div>
|
|
330
|
+
</div>
|
|
331
|
+
</div>
|
|
332
|
+
</div>
|
|
333
|
+
);
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
### 2. Responsive Design
|
|
337
|
+
|
|
338
|
+
```tsx
|
|
339
|
+
export const General = ({ product }: ProductViewProps) => (
|
|
340
|
+
<div className="animate-fadeIn">
|
|
341
|
+
<div className="flex flex-col gap-6 lg:flex-row">
|
|
342
|
+
{/* Images - full width on mobile, 40% on desktop */}
|
|
343
|
+
<div className="w-full lg:w-2/5">
|
|
344
|
+
{product.images && product.images.length > 0 ? (
|
|
345
|
+
<div className="relative aspect-square overflow-hidden rounded-lg">
|
|
346
|
+
<Image src={product.images[0].url} alt={product.name} fill className="object-cover" />
|
|
347
|
+
</div>
|
|
348
|
+
) : (
|
|
349
|
+
<div className="flex aspect-square items-center justify-center rounded-lg bg-gray-200">
|
|
350
|
+
<span className="text-gray-400">No image available</span>
|
|
351
|
+
</div>
|
|
352
|
+
)}
|
|
353
|
+
</div>
|
|
354
|
+
|
|
355
|
+
{/* Product info - full width on mobile, 60% on desktop */}
|
|
356
|
+
<div className="w-full lg:w-3/5">
|
|
357
|
+
<h1 className="text-xl font-bold md:text-2xl lg:text-3xl">{product.name}</h1>
|
|
358
|
+
<div className="text-primary mt-2 text-lg font-medium md:text-xl">{formatCurrency(product.price)}</div>
|
|
359
|
+
|
|
360
|
+
{/* Responsive grid for specifications */}
|
|
361
|
+
<div className="mt-6 grid grid-cols-1 gap-4 md:grid-cols-2">
|
|
362
|
+
{product.specifications.map((spec) => (
|
|
363
|
+
<div key={spec.id} className="border-b pb-2">
|
|
364
|
+
<span className="text-sm text-gray-500">{spec.name}:</span>
|
|
365
|
+
<div>{spec.value}</div>
|
|
366
|
+
</div>
|
|
367
|
+
))}
|
|
368
|
+
</div>
|
|
369
|
+
</div>
|
|
370
|
+
</div>
|
|
371
|
+
</div>
|
|
372
|
+
);
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
### 3. Conditional Rendering for Edge Cases
|
|
376
|
+
|
|
377
|
+
```tsx
|
|
378
|
+
export const General = ({ order }: OrderViewProps) => (
|
|
379
|
+
<div className="order-view">
|
|
380
|
+
<h1 className="text-2xl font-bold">Order #{order.number}</h1>
|
|
381
|
+
|
|
382
|
+
{/* Handle missing customer information */}
|
|
383
|
+
<div className="mt-4">
|
|
384
|
+
<h2 className="text-lg font-medium">Customer</h2>
|
|
385
|
+
{order.customer ? (
|
|
386
|
+
<div className="mt-2">
|
|
387
|
+
<p>{order.customer.name}</p>
|
|
388
|
+
<p>{order.customer.email}</p>
|
|
389
|
+
<p>{order.customer.phone || "No phone provided"}</p>
|
|
390
|
+
</div>
|
|
391
|
+
) : (
|
|
392
|
+
<p className="text-gray-500 italic">Customer information not available</p>
|
|
393
|
+
)}
|
|
394
|
+
</div>
|
|
395
|
+
|
|
396
|
+
{/* Handle empty item lists */}
|
|
397
|
+
<div className="mt-6">
|
|
398
|
+
<h2 className="text-lg font-medium">Items</h2>
|
|
399
|
+
{order.items && order.items.length > 0 ? (
|
|
400
|
+
<table className="mt-2 w-full">{/* Table content */}</table>
|
|
401
|
+
) : (
|
|
402
|
+
<p className="mt-2 text-gray-500 italic">No items in this order</p>
|
|
403
|
+
)}
|
|
404
|
+
</div>
|
|
405
|
+
|
|
406
|
+
{/* Conditional sections based on order status */}
|
|
407
|
+
{order.status === "delivered" && (
|
|
408
|
+
<div className="mt-6 rounded-lg bg-green-50 p-4">
|
|
409
|
+
<h2 className="text-lg font-medium text-green-800">Delivery Information</h2>
|
|
410
|
+
<p className="mt-2">Delivered on: {formatDate(order.deliveryDate)}</p>
|
|
411
|
+
{order.signedBy && <p>Signed by: {order.signedBy}</p>}
|
|
412
|
+
</div>
|
|
413
|
+
)}
|
|
414
|
+
</div>
|
|
415
|
+
);
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
### 4. Component Composition for Complex Views
|
|
419
|
+
|
|
420
|
+
```tsx
|
|
421
|
+
// Helper components for complex views
|
|
422
|
+
const OrderHeader = ({ order }: { order: cnst.Order }) => (
|
|
423
|
+
<div className="flex items-center justify-between">
|
|
424
|
+
<h1 className="text-2xl font-bold">Order #{order.number}</h1>
|
|
425
|
+
<StatusBadge status={order.status} />
|
|
426
|
+
</div>
|
|
427
|
+
);
|
|
428
|
+
|
|
429
|
+
const StatusBadge = ({ status }: { status: string }) => {
|
|
430
|
+
const statusStyles = {
|
|
431
|
+
pending: "bg-yellow-100 text-yellow-800",
|
|
432
|
+
processing: "bg-blue-100 text-blue-800",
|
|
433
|
+
shipped: "bg-purple-100 text-purple-800",
|
|
434
|
+
delivered: "bg-green-100 text-green-800",
|
|
435
|
+
cancelled: "bg-red-100 text-red-800",
|
|
436
|
+
};
|
|
437
|
+
|
|
438
|
+
return (
|
|
439
|
+
<span className={`rounded-full px-3 py-1 text-sm font-medium ${statusStyles[status] || "bg-gray-100"}`}>
|
|
440
|
+
{status}
|
|
441
|
+
</span>
|
|
442
|
+
);
|
|
443
|
+
};
|
|
444
|
+
|
|
445
|
+
const OrderItems = ({ items }: { items: cnst.OrderItem[] }) => (
|
|
446
|
+
<div className="mt-6">
|
|
447
|
+
<h2 className="mb-2 text-lg font-medium">Items</h2>
|
|
448
|
+
<div className="overflow-hidden rounded-lg border">
|
|
449
|
+
{items.map((item, index) => (
|
|
450
|
+
<div
|
|
451
|
+
key={item.id}
|
|
452
|
+
className={clsx("flex items-center justify-between p-4", index < items.length - 1 && "border-b")}
|
|
453
|
+
>
|
|
454
|
+
<div className="flex items-center">
|
|
455
|
+
{item.product.image && (
|
|
456
|
+
<div className="mr-4 h-12 w-12">
|
|
457
|
+
<Image src={item.product.image} alt={item.product.name} width={48} height={48} />
|
|
458
|
+
</div>
|
|
459
|
+
)}
|
|
460
|
+
<div>
|
|
461
|
+
<div className="font-medium">{item.product.name}</div>
|
|
462
|
+
<div className="text-sm text-gray-500">Qty: {item.quantity}</div>
|
|
463
|
+
</div>
|
|
464
|
+
</div>
|
|
465
|
+
<div className="text-right">
|
|
466
|
+
<div>{formatCurrency(item.price)}</div>
|
|
467
|
+
<div className="text-sm text-gray-500">{formatCurrency(item.price * item.quantity)}</div>
|
|
468
|
+
</div>
|
|
469
|
+
</div>
|
|
470
|
+
))}
|
|
471
|
+
</div>
|
|
472
|
+
</div>
|
|
473
|
+
);
|
|
474
|
+
|
|
475
|
+
// Main component combining the pieces
|
|
476
|
+
export const General = ({ order }: OrderViewProps) => (
|
|
477
|
+
<div className="space-y-6">
|
|
478
|
+
<OrderHeader order={order} />
|
|
479
|
+
<CustomerSection customer={order.customer} />
|
|
480
|
+
<OrderItems items={order.items} />
|
|
481
|
+
<ShippingSection shipping={order.shipping} />
|
|
482
|
+
<PaymentSection payment={order.payment} />
|
|
483
|
+
<TotalsSection totals={order.totals} />
|
|
484
|
+
</div>
|
|
485
|
+
);
|
|
486
|
+
```
|
|
487
|
+
|
|
488
|
+
## How to Use Model.View.tsx in Pages, Utils and Zones
|
|
489
|
+
|
|
490
|
+
### In Pages (Server Components)
|
|
491
|
+
|
|
492
|
+
```tsx
|
|
493
|
+
// app/orders/[id]/page.tsx
|
|
494
|
+
import { Order } from "@shared/client";
|
|
495
|
+
import { getOrderById } from "@shared/lib/order/order.service";
|
|
496
|
+
|
|
497
|
+
export default async function OrderPage({ params }: { params: { id: string } }) {
|
|
498
|
+
const order = await getOrderById(params.id);
|
|
499
|
+
|
|
500
|
+
return (
|
|
501
|
+
<div className="container mx-auto py-8">
|
|
502
|
+
<Order.View.General order={order} />
|
|
503
|
+
</div>
|
|
504
|
+
);
|
|
505
|
+
}
|
|
506
|
+
```
|
|
507
|
+
|
|
508
|
+
### In Utils (Client Components)
|
|
509
|
+
|
|
510
|
+
```tsx
|
|
511
|
+
// Order.Util.tsx
|
|
512
|
+
"use client";
|
|
513
|
+
import { useState } from "react";
|
|
514
|
+
import { Order } from "@shared/client";
|
|
515
|
+
import { st } from "@shared/client";
|
|
516
|
+
|
|
517
|
+
export const OrderViewer = ({ orderId }: { orderId: string }) => {
|
|
518
|
+
const order = st.use.order(orderId);
|
|
519
|
+
const [view, setView] = useState<"general" | "items" | "shipping">("general");
|
|
520
|
+
|
|
521
|
+
if (!order) return <div className="animate-pulse">Loading...</div>;
|
|
522
|
+
|
|
523
|
+
return (
|
|
524
|
+
<div>
|
|
525
|
+
<div className="tabs mb-4">
|
|
526
|
+
<button className={`tab ${view === "general" ? "tab-active" : ""}`} onClick={() => setView("general")}>
|
|
527
|
+
Overview
|
|
528
|
+
</button>
|
|
529
|
+
<button className={`tab ${view === "items" ? "tab-active" : ""}`} onClick={() => setView("items")}>
|
|
530
|
+
Items
|
|
531
|
+
</button>
|
|
532
|
+
<button className={`tab ${view === "shipping" ? "tab-active" : ""}`} onClick={() => setView("shipping")}>
|
|
533
|
+
Shipping
|
|
534
|
+
</button>
|
|
535
|
+
</div>
|
|
536
|
+
|
|
537
|
+
{view === "general" && <Order.View.General order={order} />}
|
|
538
|
+
{view === "items" && <Order.View.Items order={order} />}
|
|
539
|
+
{view === "shipping" && <Order.View.Shipping order={order} />}
|
|
540
|
+
|
|
541
|
+
<div className="mt-6 flex justify-end space-x-2">
|
|
542
|
+
<button className="btn btn-outline" onClick={() => st.do.printOrder(orderId)}>
|
|
543
|
+
Print
|
|
544
|
+
</button>
|
|
545
|
+
{order.status === "pending" && (
|
|
546
|
+
<button className="btn btn-primary" onClick={() => st.do.processOrder(orderId)}>
|
|
547
|
+
Process Order
|
|
548
|
+
</button>
|
|
549
|
+
)}
|
|
550
|
+
</div>
|
|
551
|
+
</div>
|
|
552
|
+
);
|
|
553
|
+
};
|
|
554
|
+
```
|
|
555
|
+
|
|
556
|
+
### In Zones (Layout Containers)
|
|
557
|
+
|
|
558
|
+
```tsx
|
|
559
|
+
// Dashboard.Zone.tsx
|
|
560
|
+
"use client";
|
|
561
|
+
import { useState, useEffect } from "react";
|
|
562
|
+
import { Order, Product } from "@shared/client";
|
|
563
|
+
import { sig } from "@shared/client";
|
|
564
|
+
import { Zone, Tabs } from "@util/ui";
|
|
565
|
+
|
|
566
|
+
export const AnalyticsDashboard = () => {
|
|
567
|
+
const [data, setData] = useState(null);
|
|
568
|
+
const [loading, setLoading] = useState(true);
|
|
569
|
+
|
|
570
|
+
useEffect(() => {
|
|
571
|
+
const loadData = async () => {
|
|
572
|
+
setLoading(true);
|
|
573
|
+
try {
|
|
574
|
+
const dashboardData = await sig.analytics.getDashboardData();
|
|
575
|
+
setData(dashboardData);
|
|
576
|
+
} catch (error) {
|
|
577
|
+
console.error("Failed to load dashboard data", error);
|
|
578
|
+
} finally {
|
|
579
|
+
setLoading(false);
|
|
580
|
+
}
|
|
581
|
+
};
|
|
582
|
+
|
|
583
|
+
loadData();
|
|
584
|
+
}, []);
|
|
585
|
+
|
|
586
|
+
if (loading) return <div className="animate-pulse">Loading dashboard...</div>;
|
|
587
|
+
if (!data) return <div>Failed to load dashboard data</div>;
|
|
588
|
+
|
|
589
|
+
return (
|
|
590
|
+
<Zone title="Business Analytics">
|
|
591
|
+
<div className="grid grid-cols-1 gap-6 lg:grid-cols-2">
|
|
592
|
+
<Zone.Section title="Recent Orders">
|
|
593
|
+
{data.recentOrders.map((order) => (
|
|
594
|
+
<div key={order.id} className="mb-4">
|
|
595
|
+
<Order.View.Summary order={order} />
|
|
596
|
+
</div>
|
|
597
|
+
))}
|
|
598
|
+
</Zone.Section>
|
|
599
|
+
|
|
600
|
+
<Zone.Section title="Top Products">
|
|
601
|
+
{data.topProducts.map((product) => (
|
|
602
|
+
<div key={product.id} className="mb-4">
|
|
603
|
+
<Product.View.Performance product={product} />
|
|
604
|
+
</div>
|
|
605
|
+
))}
|
|
606
|
+
</Zone.Section>
|
|
607
|
+
</div>
|
|
608
|
+
|
|
609
|
+
<Zone.Section title="Sales Overview">
|
|
610
|
+
<SalesChart data={data.salesData} />
|
|
611
|
+
</Zone.Section>
|
|
612
|
+
</Zone>
|
|
613
|
+
);
|
|
614
|
+
};
|
|
615
|
+
```
|
|
616
|
+
|
|
617
|
+
## Common Patterns and Advanced Techniques
|
|
618
|
+
|
|
619
|
+
### 1. Status-Based Visualizations
|
|
620
|
+
|
|
621
|
+
```tsx
|
|
622
|
+
export const StatusSummary = ({ project }: ProjectViewProps) => {
|
|
623
|
+
// Determine color based on status
|
|
624
|
+
const statusColor =
|
|
625
|
+
{
|
|
626
|
+
planning: "bg-blue-100 text-blue-800",
|
|
627
|
+
active: "bg-green-100 text-green-800",
|
|
628
|
+
onHold: "bg-yellow-100 text-yellow-800",
|
|
629
|
+
completed: "bg-purple-100 text-purple-800",
|
|
630
|
+
cancelled: "bg-red-100 text-red-800",
|
|
631
|
+
}[project.status] || "bg-gray-100 text-gray-800";
|
|
632
|
+
|
|
633
|
+
// Calculate progress percentage
|
|
634
|
+
const progress = (project.completedTasks / (project.totalTasks || 1)) * 100;
|
|
635
|
+
|
|
636
|
+
return (
|
|
637
|
+
<div className="rounded-lg border p-4">
|
|
638
|
+
<div className="mb-4 flex items-center justify-between">
|
|
639
|
+
<h2 className="text-lg font-bold">{project.name}</h2>
|
|
640
|
+
<span className={`rounded-full px-3 py-1 text-sm font-medium ${statusColor}`}>{project.status}</span>
|
|
641
|
+
</div>
|
|
642
|
+
|
|
643
|
+
<div className="space-y-2">
|
|
644
|
+
<div className="flex justify-between text-sm">
|
|
645
|
+
<span>Progress</span>
|
|
646
|
+
<span>{Math.round(progress)}%</span>
|
|
647
|
+
</div>
|
|
648
|
+
<div className="h-2.5 w-full rounded-full bg-gray-200">
|
|
649
|
+
<div className="h-2.5 rounded-full bg-blue-600" style={{ width: `${progress}%` }}></div>
|
|
650
|
+
</div>
|
|
651
|
+
</div>
|
|
652
|
+
|
|
653
|
+
<div className="mt-4 grid grid-cols-2 gap-4 text-center">
|
|
654
|
+
<div>
|
|
655
|
+
<div className="text-sm text-gray-500">Tasks</div>
|
|
656
|
+
<div className="font-bold">
|
|
657
|
+
{project.completedTasks}/{project.totalTasks}
|
|
658
|
+
</div>
|
|
659
|
+
</div>
|
|
660
|
+
<div>
|
|
661
|
+
<div className="text-sm text-gray-500">Due Date</div>
|
|
662
|
+
<div className="font-bold">{formatDate(project.dueDate)}</div>
|
|
663
|
+
</div>
|
|
664
|
+
</div>
|
|
665
|
+
</div>
|
|
666
|
+
);
|
|
667
|
+
};
|
|
668
|
+
```
|
|
669
|
+
|
|
670
|
+
### 2. Hierarchical Data Display
|
|
671
|
+
|
|
672
|
+
```tsx
|
|
673
|
+
export const OrgChart = ({ department }: DepartmentViewProps) => {
|
|
674
|
+
// Recursive component for displaying a department and its subdepartments
|
|
675
|
+
const DepartmentNode = ({ dept, level = 0 }: { dept: cnst.Department; level: number }) => (
|
|
676
|
+
<div className={`pl-${level * 6}`}>
|
|
677
|
+
<div className="my-1 flex items-center rounded border p-2">
|
|
678
|
+
<div className="font-bold">{dept.name}</div>
|
|
679
|
+
<div className="ml-2 text-sm text-gray-500">({dept.employeeCount} employees)</div>
|
|
680
|
+
</div>
|
|
681
|
+
|
|
682
|
+
{dept.subdepartments?.map((subdept) => <DepartmentNode key={subdept.id} dept={subdept} level={level + 1} />)}
|
|
683
|
+
</div>
|
|
684
|
+
);
|
|
685
|
+
|
|
686
|
+
return (
|
|
687
|
+
<div className="department-hierarchy">
|
|
688
|
+
<h2 className="mb-4 text-xl font-bold">Organization Structure</h2>
|
|
689
|
+
<DepartmentNode dept={department} level={0} />
|
|
690
|
+
</div>
|
|
691
|
+
);
|
|
692
|
+
};
|
|
693
|
+
```
|
|
694
|
+
|
|
695
|
+
### 3. Multi-Section Views with Tabs
|
|
696
|
+
|
|
697
|
+
```tsx
|
|
698
|
+
export const Product = ({ product }: ProductViewProps) => (
|
|
699
|
+
<div className="product-view">
|
|
700
|
+
<h1 className="mb-6 text-2xl font-bold">{product.name}</h1>
|
|
701
|
+
|
|
702
|
+
<div className="tabs tabs-boxed mb-6">
|
|
703
|
+
<a className="tab tab-active">Overview</a>
|
|
704
|
+
<a className="tab">Specifications</a>
|
|
705
|
+
<a className="tab">Reviews</a>
|
|
706
|
+
</div>
|
|
707
|
+
|
|
708
|
+
<div className="tab-content">
|
|
709
|
+
{/* Overview section - active by default */}
|
|
710
|
+
<div className="space-y-4">
|
|
711
|
+
<div className="flex flex-col gap-6 md:flex-row">
|
|
712
|
+
{/* Product image */}
|
|
713
|
+
<div className="md:w-1/2">
|
|
714
|
+
{product.image && (
|
|
715
|
+
<div className="aspect-square overflow-hidden rounded-lg">
|
|
716
|
+
<Image src={product.image} alt={product.name} fill className="object-contain" />
|
|
717
|
+
</div>
|
|
718
|
+
)}
|
|
719
|
+
</div>
|
|
720
|
+
|
|
721
|
+
{/* Product details */}
|
|
722
|
+
<div className="md:w-1/2">
|
|
723
|
+
<div className="text-primary text-xl font-bold">{formatCurrency(product.price)}</div>
|
|
724
|
+
|
|
725
|
+
<div className="mt-4">
|
|
726
|
+
<h2 className="mb-2 text-lg font-medium">Description</h2>
|
|
727
|
+
<p>{product.description}</p>
|
|
728
|
+
</div>
|
|
729
|
+
|
|
730
|
+
<div className="mt-4">
|
|
731
|
+
<h2 className="mb-2 text-lg font-medium">Features</h2>
|
|
732
|
+
<ul className="list-disc space-y-1 pl-5">
|
|
733
|
+
{product.features?.map((feature, index) => <li key={index}>{feature}</li>)}
|
|
734
|
+
</ul>
|
|
735
|
+
</div>
|
|
736
|
+
</div>
|
|
737
|
+
</div>
|
|
738
|
+
</div>
|
|
739
|
+
|
|
740
|
+
{/* Other tab contents would be here but hidden initially */}
|
|
741
|
+
</div>
|
|
742
|
+
</div>
|
|
743
|
+
);
|
|
744
|
+
```
|
|
745
|
+
|
|
746
|
+
## Performance Optimization Strategies
|
|
747
|
+
|
|
748
|
+
### 1. Optimize Images and Media
|
|
749
|
+
|
|
750
|
+
```tsx
|
|
751
|
+
import { Image } from "@util/ui"; // Optimized image component
|
|
752
|
+
|
|
753
|
+
export const General = ({ product }: ProductViewProps) => (
|
|
754
|
+
<div className="product-view">
|
|
755
|
+
{product.image && (
|
|
756
|
+
<Image
|
|
757
|
+
src={product.image}
|
|
758
|
+
alt={product.name}
|
|
759
|
+
width={600}
|
|
760
|
+
height={600}
|
|
761
|
+
sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
|
|
762
|
+
priority={false}
|
|
763
|
+
loading="lazy"
|
|
764
|
+
className="h-auto w-full rounded-lg object-contain"
|
|
765
|
+
/>
|
|
766
|
+
)}
|
|
767
|
+
{/* Rest of the product view */}
|
|
768
|
+
</div>
|
|
769
|
+
);
|
|
770
|
+
```
|
|
771
|
+
|
|
772
|
+
### 2. Conditional Rendering for Complex Content
|
|
773
|
+
|
|
774
|
+
```tsx
|
|
775
|
+
export const Dashboard = ({ project }: ProjectViewProps) => (
|
|
776
|
+
<div className="project-dashboard">
|
|
777
|
+
{/* Always render essential information */}
|
|
778
|
+
<div className="mb-6">
|
|
779
|
+
<h1 className="text-2xl font-bold">{project.name}</h1>
|
|
780
|
+
<div className="mt-2 flex gap-2">
|
|
781
|
+
<span className={`badge ${getStatusBadgeClass(project.status)}`}>{project.status}</span>
|
|
782
|
+
<span className="text-sm text-gray-500">Due: {formatDate(project.dueDate)}</span>
|
|
783
|
+
</div>
|
|
784
|
+
</div>
|
|
785
|
+
|
|
786
|
+
{/* Only render charts if project has tasks */}
|
|
787
|
+
{project.tasks?.length > 0 && (
|
|
788
|
+
<div className="mb-6">
|
|
789
|
+
<h2 className="mb-2 text-lg font-medium">Progress Overview</h2>
|
|
790
|
+
<ProgressChart tasks={project.tasks} />
|
|
791
|
+
</div>
|
|
792
|
+
)}
|
|
793
|
+
|
|
794
|
+
{/* Only render team section if project has team members */}
|
|
795
|
+
{project.team?.length > 0 && (
|
|
796
|
+
<div className="mb-6">
|
|
797
|
+
<h2 className="mb-2 text-lg font-medium">Team</h2>
|
|
798
|
+
<div className="flex flex-wrap gap-2">
|
|
799
|
+
{project.team.map((member) => (
|
|
800
|
+
<TeamMemberBadge key={member.id} member={member} />
|
|
801
|
+
))}
|
|
802
|
+
</div>
|
|
803
|
+
</div>
|
|
804
|
+
)}
|
|
805
|
+
</div>
|
|
806
|
+
);
|
|
807
|
+
```
|
|
808
|
+
|
|
809
|
+
### 3. Chunking Large Data Displays
|
|
810
|
+
|
|
811
|
+
```tsx
|
|
812
|
+
export const OrderList = ({ orders }: { orders: cnst.Order[] }) => (
|
|
813
|
+
<div className="space-y-6">
|
|
814
|
+
<h2 className="text-xl font-bold">Orders ({orders.length})</h2>
|
|
815
|
+
|
|
816
|
+
{/* Group orders by month for better organization */}
|
|
817
|
+
{Object.entries(groupByMonth(orders)).map(([month, monthOrders]) => (
|
|
818
|
+
<div key={month}>
|
|
819
|
+
<h3 className="mb-2 text-lg font-medium">{month}</h3>
|
|
820
|
+
<div className="space-y-2">
|
|
821
|
+
{monthOrders.map((order) => (
|
|
822
|
+
<Order.View.Summary key={order.id} order={order} />
|
|
823
|
+
))}
|
|
824
|
+
</div>
|
|
825
|
+
</div>
|
|
826
|
+
))}
|
|
827
|
+
</div>
|
|
828
|
+
);
|
|
829
|
+
|
|
830
|
+
// Helper function to group orders by month
|
|
831
|
+
function groupByMonth(orders: cnst.Order[]) {
|
|
832
|
+
return orders.reduce((groups, order) => {
|
|
833
|
+
const month = new Date(order.createdAt).toLocaleString("default", { month: "long", year: "numeric" });
|
|
834
|
+
if (!groups[month]) groups[month] = [];
|
|
835
|
+
groups[month].push(order);
|
|
836
|
+
return groups;
|
|
837
|
+
}, {});
|
|
838
|
+
}
|
|
839
|
+
```
|
|
840
|
+
|
|
841
|
+
## Troubleshooting and Common Issues
|
|
842
|
+
|
|
843
|
+
### 1. "useState is not defined" or "use client" Error
|
|
844
|
+
|
|
845
|
+
**Problem**: Server component using client-side hooks
|
|
846
|
+
|
|
847
|
+
```tsx
|
|
848
|
+
// ❌ Error: This is a server component that's using client-side hooks
|
|
849
|
+
export const ProductView = ({ product }) => {
|
|
850
|
+
const [expanded, setExpanded] = useState(false);
|
|
851
|
+
// ...
|
|
852
|
+
};
|
|
853
|
+
```
|
|
854
|
+
|
|
855
|
+
**Solution**: Move interactive elements to client components
|
|
856
|
+
|
|
857
|
+
```tsx
|
|
858
|
+
// ✅ Server component (View.tsx) - pure presentation
|
|
859
|
+
export const ProductView = ({ product, expanded = false }) => (
|
|
860
|
+
<div>
|
|
861
|
+
<h1>{product.name}</h1>
|
|
862
|
+
{expanded && <div className="mt-4">{/* Additional details */}</div>}
|
|
863
|
+
</div>
|
|
864
|
+
);
|
|
865
|
+
|
|
866
|
+
// In a separate Util.tsx file (client component)
|
|
867
|
+
("use client");
|
|
868
|
+
import { useState } from "react";
|
|
869
|
+
import { Product } from "@your-lib/client";
|
|
870
|
+
|
|
871
|
+
export const ExpandableProduct = ({ product }) => {
|
|
872
|
+
const [expanded, setExpanded] = useState(false);
|
|
873
|
+
|
|
874
|
+
return (
|
|
875
|
+
<div>
|
|
876
|
+
<Product.View.ProductView product={product} expanded={expanded} />
|
|
877
|
+
<button onClick={() => setExpanded(!expanded)}>{expanded ? "Show Less" : "Show More"}</button>
|
|
878
|
+
</div>
|
|
879
|
+
);
|
|
880
|
+
};
|
|
881
|
+
```
|
|
882
|
+
|
|
883
|
+
### 2. Missing or Undefined Data
|
|
884
|
+
|
|
885
|
+
**Problem**: Trying to access properties on potentially undefined data
|
|
886
|
+
|
|
887
|
+
```tsx
|
|
888
|
+
// ❌ Error: Cannot read properties of undefined (reading 'name')
|
|
889
|
+
export const CustomerView = ({ order }) => (
|
|
890
|
+
<div>
|
|
891
|
+
<h2>Customer</h2>
|
|
892
|
+
<p>{order.customer.name}</p> // Error if customer is undefined
|
|
893
|
+
<p>{order.customer.email}</p>
|
|
894
|
+
</div>
|
|
895
|
+
);
|
|
896
|
+
```
|
|
897
|
+
|
|
898
|
+
**Solution**: Add proper null checks and fallbacks
|
|
899
|
+
|
|
900
|
+
```tsx
|
|
901
|
+
// ✅ With proper null checks
|
|
902
|
+
export const CustomerView = ({ order }) => (
|
|
903
|
+
<div>
|
|
904
|
+
<h2>Customer</h2>
|
|
905
|
+
{order.customer ? (
|
|
906
|
+
<>
|
|
907
|
+
<p>{order.customer.name}</p>
|
|
908
|
+
<p>{order.customer.email || "No email provided"}</p>
|
|
909
|
+
</>
|
|
910
|
+
) : (
|
|
911
|
+
<p className="text-gray-500 italic">No customer information available</p>
|
|
912
|
+
)}
|
|
913
|
+
</div>
|
|
914
|
+
);
|
|
915
|
+
```
|
|
916
|
+
|
|
917
|
+
### 3. Layout Shifts Due to Conditional Rendering
|
|
918
|
+
|
|
919
|
+
**Problem**: Content jumps when conditional elements render
|
|
920
|
+
|
|
921
|
+
**Solution**: Use fixed-height placeholders or layout containers
|
|
922
|
+
|
|
923
|
+
```tsx
|
|
924
|
+
export const ProductDetails = ({ product }: ProductViewProps) => (
|
|
925
|
+
<div className="product-details">
|
|
926
|
+
{/* Images - maintain consistent height */}
|
|
927
|
+
<div className="aspect-square overflow-hidden rounded-lg bg-gray-100">
|
|
928
|
+
{product.image ? (
|
|
929
|
+
<Image src={product.image} alt={product.name} fill className="object-cover" />
|
|
930
|
+
) : (
|
|
931
|
+
<div className="flex h-full items-center justify-center">
|
|
932
|
+
<span className="text-gray-400">No image available</span>
|
|
933
|
+
</div>
|
|
934
|
+
)}
|
|
935
|
+
</div>
|
|
936
|
+
|
|
937
|
+
{/* Product information with consistent spacing */}
|
|
938
|
+
<div className="mt-4 min-h-[200px]">
|
|
939
|
+
<h1 className="text-xl font-bold">{product.name}</h1>
|
|
940
|
+
<div className="text-primary mt-2 font-bold">{formatCurrency(product.price)}</div>
|
|
941
|
+
|
|
942
|
+
<div className="mt-4 h-[100px] overflow-auto">
|
|
943
|
+
{product.description ? (
|
|
944
|
+
<p>{product.description}</p>
|
|
945
|
+
) : (
|
|
946
|
+
<p className="text-gray-500 italic">No description available</p>
|
|
947
|
+
)}
|
|
948
|
+
</div>
|
|
949
|
+
</div>
|
|
950
|
+
</div>
|
|
951
|
+
);
|
|
952
|
+
```
|
|
953
|
+
|
|
954
|
+
### 4. Over-Fetching Data in Nested Views
|
|
955
|
+
|
|
956
|
+
**Problem**: Nested components causing excessive data fetching
|
|
957
|
+
|
|
958
|
+
**Solution**: Pass only required data from parent to child components
|
|
959
|
+
|
|
960
|
+
```tsx
|
|
961
|
+
// Parent component passes only necessary data
|
|
962
|
+
export const OrderView = ({ order }: OrderViewProps) => (
|
|
963
|
+
<div className="order-view">
|
|
964
|
+
<h1>Order #{order.number}</h1>
|
|
965
|
+
|
|
966
|
+
<div className="mt-6">
|
|
967
|
+
<h2>Customer</h2>
|
|
968
|
+
<CustomerSummary customer={order.customer} />
|
|
969
|
+
</div>
|
|
970
|
+
|
|
971
|
+
<div className="mt-6">
|
|
972
|
+
<h2>Items</h2>
|
|
973
|
+
{order.items.map((item) => (
|
|
974
|
+
<OrderItemRow
|
|
975
|
+
key={item.id}
|
|
976
|
+
productName={item.product.name}
|
|
977
|
+
quantity={item.quantity}
|
|
978
|
+
price={item.price}
|
|
979
|
+
// Only pass needed fields, not the entire product object
|
|
980
|
+
/>
|
|
981
|
+
))}
|
|
982
|
+
</div>
|
|
983
|
+
</div>
|
|
984
|
+
);
|
|
985
|
+
```
|
|
986
|
+
|
|
987
|
+
## Conclusion
|
|
988
|
+
|
|
989
|
+
Model.View.tsx components are a fundamental building block in the Akan.js component architecture, providing consistent, reusable presentation layers for domain models. By following the guidelines in this document, you'll create View components that:
|
|
990
|
+
|
|
991
|
+
- Provide comprehensive visual representations of domain models
|
|
992
|
+
- Work seamlessly with other components in the Akan.js ecosystem
|
|
993
|
+
- Leverage the performance benefits of Next.js server components
|
|
994
|
+
- Are maintainable, scalable, and follow consistent patterns
|
|
995
|
+
|
|
996
|
+
Remember the key principles:
|
|
997
|
+
|
|
998
|
+
1. Focus on presentation, not interaction
|
|
999
|
+
2. Follow server component constraints
|
|
1000
|
+
3. Organize in logical component variations
|
|
1001
|
+
4. Design for reuse across pages, Utils, and Zones
|
|
1002
|
+
5. Handle edge cases and provide fallbacks
|
|
1003
|
+
6. Optimize for performance and accessibility
|
|
1004
|
+
|
|
1005
|
+
With these principles in mind, your Model.View.tsx components will provide a solid foundation for your Akan.js application's user interface.
|