@mostrom/app-shell 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/ralph-loop.local.md +9 -0
- package/README.md +172 -0
- package/bin/init.js +269 -0
- package/bun.lock +401 -0
- package/components.json +28 -0
- package/package.json +74 -0
- package/scripts/publish-npm.sh +202 -0
- package/src/AppShell.tsx +847 -0
- package/src/components/PageHeader.tsx +160 -0
- package/src/components/data-table/README.md +447 -0
- package/src/components/data-table/data-table-preferences.tsx +184 -0
- package/src/components/data-table/data-table-toolbar.tsx +118 -0
- package/src/components/data-table/data-table.tsx +37 -0
- package/src/components/data-table/index.ts +32 -0
- package/src/components/global-header/AllServicesButton.tsx +127 -0
- package/src/components/global-header/CategoriesButton.tsx +120 -0
- package/src/components/global-header/GlobalHeader.tsx +59 -0
- package/src/components/global-header/GlobalHeaderSearch.tsx +57 -0
- package/src/components/global-header/HeaderUtilities.tsx +243 -0
- package/src/components/global-header/ServicesMenu.tsx +246 -0
- package/src/components/layout/AppBreadcrumb.tsx +70 -0
- package/src/components/layout/AppFlashbar.tsx +95 -0
- package/src/components/layout/AppLayout.tsx +271 -0
- package/src/components/layout/AppNavigation.tsx +313 -0
- package/src/components/layout/AppSidebar.tsx +229 -0
- package/src/components/patterns/index.ts +14 -0
- package/src/components/patterns/p-alert-5.tsx +19 -0
- package/src/components/patterns/p-autocomplete-5.tsx +89 -0
- package/src/components/patterns/p-breadcrumb-1.tsx +28 -0
- package/src/components/patterns/p-button-42.tsx +37 -0
- package/src/components/patterns/p-button-51.tsx +14 -0
- package/src/components/patterns/p-button-6.tsx +5 -0
- package/src/components/patterns/p-calendar-1.tsx +18 -0
- package/src/components/patterns/p-card-1.tsx +33 -0
- package/src/components/patterns/p-card-2.tsx +26 -0
- package/src/components/patterns/p-card-5.tsx +31 -0
- package/src/components/patterns/p-collapsible-7.tsx +121 -0
- package/src/components/patterns/p-command-6.tsx +113 -0
- package/src/components/patterns/p-dialog-1.tsx +56 -0
- package/src/components/patterns/p-dropdown-menu-1.tsx +38 -0
- package/src/components/patterns/p-dropdown-menu-11.tsx +122 -0
- package/src/components/patterns/p-dropdown-menu-14.tsx +165 -0
- package/src/components/patterns/p-dropdown-menu-9.tsx +108 -0
- package/src/components/patterns/p-empty-2.tsx +34 -0
- package/src/components/patterns/p-file-upload-1.tsx +72 -0
- package/src/components/patterns/p-filters-1.tsx +666 -0
- package/src/components/patterns/p-frame-2.tsx +26 -0
- package/src/components/patterns/p-tabs-2.tsx +129 -0
- package/src/components/reui/alert.tsx +92 -0
- package/src/components/reui/autocomplete.tsx +343 -0
- package/src/components/reui/badge.tsx +87 -0
- package/src/components/reui/data-grid/data-grid-column-filter.tsx +165 -0
- package/src/components/reui/data-grid/data-grid-column-header.tsx +339 -0
- package/src/components/reui/data-grid/data-grid-column-visibility.tsx +55 -0
- package/src/components/reui/data-grid/data-grid-pagination.tsx +224 -0
- package/src/components/reui/data-grid/data-grid-table-dnd-rows.tsx +260 -0
- package/src/components/reui/data-grid/data-grid-table-dnd.tsx +253 -0
- package/src/components/reui/data-grid/data-grid-table.tsx +639 -0
- package/src/components/reui/data-grid/data-grid.tsx +209 -0
- package/src/components/reui/date-selector.tsx +1330 -0
- package/src/components/reui/filters.tsx +1869 -0
- package/src/components/reui/frame.tsx +134 -0
- package/src/components/reui/index.ts +17 -0
- package/src/components/reui/timeline.tsx +219 -0
- package/src/components/search/Autocomplete.tsx +183 -0
- package/src/components/search/AutocompleteClient.tsx +293 -0
- package/src/components/search/GlobalSearch.tsx +187 -0
- package/src/components/section-drawer/deal-drawer-content.tsx +891 -0
- package/src/components/section-drawer/index.ts +19 -0
- package/src/components/section-drawer/section-drawer.css +665 -0
- package/src/components/section-drawer/section-drawer.tsx +467 -0
- package/src/components/sectioned-list-board/README.md +78 -0
- package/src/components/sectioned-list-board/board-card-content.tsx +340 -0
- package/src/components/sectioned-list-board/date-range-filter.tsx +249 -0
- package/src/components/sectioned-list-board/index.ts +19 -0
- package/src/components/sectioned-list-board/sectioned-list-board.css +564 -0
- package/src/components/sectioned-list-board/sectioned-list-board.tsx +731 -0
- package/src/components/sectioned-list-board/sortable-card.tsx +314 -0
- package/src/components/sectioned-list-board/sortable-section.tsx +319 -0
- package/src/components/sectioned-list-board/types.ts +216 -0
- package/src/components/sectioned-list-table/README.md +80 -0
- package/src/components/sectioned-list-table/index.ts +14 -0
- package/src/components/sectioned-list-table/sectioned-list-table.css +534 -0
- package/src/components/sectioned-list-table/sectioned-list-table.tsx +740 -0
- package/src/components/sectioned-list-table/sortable-column-header.tsx +120 -0
- package/src/components/sectioned-list-table/sortable-row.tsx +420 -0
- package/src/components/sectioned-list-table/sortable-section.tsx +251 -0
- package/src/components/sectioned-list-table/table-cell-content.tsx +129 -0
- package/src/components/sectioned-list-table/types.ts +120 -0
- package/src/components/sectioned-list-table/use-column-preferences.ts +103 -0
- package/src/components/ui/actions-dropdown.tsx +109 -0
- package/src/components/ui/assignee-selector.tsx +209 -0
- package/src/components/ui/avatar.tsx +107 -0
- package/src/components/ui/breadcrumb.tsx +109 -0
- package/src/components/ui/button-group.tsx +83 -0
- package/src/components/ui/button.tsx +64 -0
- package/src/components/ui/calendar.tsx +220 -0
- package/src/components/ui/card.tsx +92 -0
- package/src/components/ui/chart.tsx +376 -0
- package/src/components/ui/checkbox.tsx +30 -0
- package/src/components/ui/collapsible.tsx +33 -0
- package/src/components/ui/command.tsx +182 -0
- package/src/components/ui/context-menu.tsx +250 -0
- package/src/components/ui/create-button-group.tsx +128 -0
- package/src/components/ui/dialog.tsx +156 -0
- package/src/components/ui/drawer.tsx +133 -0
- package/src/components/ui/dropdown-menu.tsx +255 -0
- package/src/components/ui/empty.tsx +104 -0
- package/src/components/ui/field.tsx +248 -0
- package/src/components/ui/form.tsx +165 -0
- package/src/components/ui/index.ts +37 -0
- package/src/components/ui/input-group.tsx +168 -0
- package/src/components/ui/input.tsx +21 -0
- package/src/components/ui/kbd.tsx +28 -0
- package/src/components/ui/label.tsx +22 -0
- package/src/components/ui/navigation-menu.tsx +168 -0
- package/src/components/ui/page-header.tsx +80 -0
- package/src/components/ui/popover.tsx +87 -0
- package/src/components/ui/scroll-area.tsx +56 -0
- package/src/components/ui/select.tsx +190 -0
- package/src/components/ui/separator.tsx +26 -0
- package/src/components/ui/sheet.tsx +141 -0
- package/src/components/ui/sidebar.tsx +726 -0
- package/src/components/ui/skeleton.tsx +13 -0
- package/src/components/ui/sonner.tsx +38 -0
- package/src/components/ui/switch.tsx +33 -0
- package/src/components/ui/tabs.tsx +91 -0
- package/src/components/ui/textarea.tsx +18 -0
- package/src/components/ui/toggle-group.tsx +83 -0
- package/src/components/ui/toggle.tsx +45 -0
- package/src/components/ui/tooltip.tsx +57 -0
- package/src/hooks/use-copy-to-clipboard.ts +37 -0
- package/src/hooks/use-file-upload.ts +415 -0
- package/src/hooks/use-mobile.ts +19 -0
- package/src/index.ts +95 -0
- package/src/lib/utils.ts +6 -0
- package/src/styles.css +1859 -0
- package/src/urls.ts +83 -0
- package/src/vite.d.ts +22 -0
- package/src/vite.js +241 -0
- package/tsconfig.base.json +18 -0
- package/tsconfig.json +24 -0
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
---
|
|
2
|
+
active: true
|
|
3
|
+
iteration: 1
|
|
4
|
+
max_iterations: 0
|
|
5
|
+
completion_promise: null
|
|
6
|
+
started_at: "2026-02-13T12:51:42Z"
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
Without diverging from the drawer requirements in @docs/prompts/todo/drawer-fields.md ensure the drawer layout wise and style wise mimics this exact image docs/prompts/todo/Screenshot 2026-02-12 at 18.19.47.png and use playwright to take screenshots and do a comparison until the layouts match 100%
|
package/README.md
ADDED
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
# App Shell
|
|
2
|
+
|
|
3
|
+
Shared Cloudscape-based layout shell used by all platform services. Mirrors the AWS Console layout pattern.
|
|
4
|
+
|
|
5
|
+
## Publishing to npm
|
|
6
|
+
|
|
7
|
+
Use the publish helper to publish both packages in order:
|
|
8
|
+
|
|
9
|
+
1. `@mostrom/service-catalog`
|
|
10
|
+
2. `@mostrom/app-shell`
|
|
11
|
+
|
|
12
|
+
From `platform/shared/packages/app-shell`:
|
|
13
|
+
|
|
14
|
+
Default scope is `@mostrom` (override with `--scope` if needed).
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
# Dry run
|
|
18
|
+
npm run publish:npm:dry-run -- --version 0.1.0
|
|
19
|
+
# or: sh scripts/publish-npm.sh --version 0.1.0 --dry-run
|
|
20
|
+
|
|
21
|
+
# Publish to latest
|
|
22
|
+
npm run publish:npm -- --version 0.1.0
|
|
23
|
+
# or: sh scripts/publish-npm.sh --version 0.1.0
|
|
24
|
+
|
|
25
|
+
# Publish to custom tag
|
|
26
|
+
npm run publish:npm -- --version 0.1.0 --tag next
|
|
27
|
+
# or: sh scripts/publish-npm.sh --version 0.1.0 --tag next
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
The script reads `NODE_AUTH_TOKEN` from environment variables, and also checks `app-shell/.env` for that key.
|
|
31
|
+
|
|
32
|
+
## Quick Start
|
|
33
|
+
|
|
34
|
+
### Initial Setup (New Apps)
|
|
35
|
+
|
|
36
|
+
Run the init script to configure a new app to use app-shell:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
# From your app's frontend directory, run the init script directly
|
|
40
|
+
node ../../../shared/packages/app-shell/bin/init.js
|
|
41
|
+
|
|
42
|
+
# Force overwrite existing files
|
|
43
|
+
node ../../../shared/packages/app-shell/bin/init.js --force
|
|
44
|
+
|
|
45
|
+
# Alternative: If app-shell is linked in node_modules, use the bin
|
|
46
|
+
./node_modules/.bin/app-shell-init
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
> **Note:** In this monorepo, prefer running the local init script directly. If you publish to npm, consumers can also use the installed bin (`app-shell-init`).
|
|
50
|
+
|
|
51
|
+
The init script creates/updates:
|
|
52
|
+
|
|
53
|
+
- `app/tailwind.css` - Imports app-shell styles
|
|
54
|
+
- `tailwind.config.ts` - Configured to scan app-shell components
|
|
55
|
+
- `components.json` - shadcn/ui configuration
|
|
56
|
+
- `app/lib/utils.ts` - Utility functions (cn, getBasePathHref)
|
|
57
|
+
|
|
58
|
+
### When to Run Init
|
|
59
|
+
|
|
60
|
+
| Scenario | Run Init? |
|
|
61
|
+
| ------------------------------------------- | ---------------------------------------------------- |
|
|
62
|
+
| Setting up a new app | Yes |
|
|
63
|
+
| CSS changes in app-shell | No - changes are picked up automatically |
|
|
64
|
+
| Adding new shadcn components | No - use `npx shadcn@latest add <component>` |
|
|
65
|
+
| Tailwind not scanning app-shell classes | Yes, with `--force` to regenerate tailwind.config.ts |
|
|
66
|
+
| Upgrading app-shell with new required files | Yes, with `--force` |
|
|
67
|
+
|
|
68
|
+
### Troubleshooting
|
|
69
|
+
|
|
70
|
+
**Styles not applying from app-shell:**
|
|
71
|
+
|
|
72
|
+
1. Ensure `tailwind.config.ts` includes the app-shell path in `content`:
|
|
73
|
+
```ts
|
|
74
|
+
content: [
|
|
75
|
+
"./app/**/*.{ts,tsx,js,jsx}",
|
|
76
|
+
"../../../shared/packages/app-shell/src/**/*.{ts,tsx}",
|
|
77
|
+
],
|
|
78
|
+
```
|
|
79
|
+
2. Run `node ../../../shared/packages/app-shell/bin/init.js --force` to regenerate config files
|
|
80
|
+
|
|
81
|
+
**Conflicting CSS (wrong colors, broken layout):**
|
|
82
|
+
|
|
83
|
+
- Check for `app/app.css` or similar files with `html`, `body`, or `:root` blocks that override app-shell variables
|
|
84
|
+
- Remove hardcoded background-color/color declarations from global CSS
|
|
85
|
+
|
|
86
|
+
## Layout Structure
|
|
87
|
+
|
|
88
|
+
```
|
|
89
|
+
+------------------------------------------------------------------+
|
|
90
|
+
| GLOBAL HEADER |
|
|
91
|
+
| [Logo] [Search Bar........................] [Apps] [Gear] [User] |
|
|
92
|
+
+------------------------------------------------------------------+
|
|
93
|
+
| SERVICE HEADER |
|
|
94
|
+
| [Menu] [Service Name] [Breadcrumbs................] [Right Panel]|
|
|
95
|
+
+--------+-----------------------------------------+---------------+
|
|
96
|
+
| | | |
|
|
97
|
+
| LEFT | MAIN CONTENT | RIGHT |
|
|
98
|
+
| PANEL | | PANEL |
|
|
99
|
+
| | (children) | |
|
|
100
|
+
| | | |
|
|
101
|
+
+--------+-----------------------------------------+---------------+
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Component Map (Cloudscape)
|
|
105
|
+
|
|
106
|
+
### Global Header (top-most, full width)
|
|
107
|
+
|
|
108
|
+
Cloudscape `TopNavigation`.
|
|
109
|
+
|
|
110
|
+
| Position | Element | Cloudscape Component | Behavior |
|
|
111
|
+
| -------- | -------- | ------------------------------------------------------ | ---------------------- |
|
|
112
|
+
| Left | Logo | `TopNavigation` `identity` | Navigate to home |
|
|
113
|
+
| Center | Search | `TopNavigation` `search` with `Input` or `Autosuggest` | Global search |
|
|
114
|
+
| Right | Apps | `TopNavigation` utility with `Icon` `grid-view` | Opens dropdown (empty) |
|
|
115
|
+
| Right | Settings | `TopNavigation` utility with `Icon` `settings` | Opens dropdown (empty) |
|
|
116
|
+
| Right | User | `TopNavigation` utility with `Icon` `user-profile` | User menu |
|
|
117
|
+
|
|
118
|
+
### Service Header (below Global Header, full width)
|
|
119
|
+
|
|
120
|
+
Cloudscape `AppLayoutToolbar`.
|
|
121
|
+
|
|
122
|
+
| Position | Element | Cloudscape Component | Behavior |
|
|
123
|
+
| -------- | ------------ | ------------------------------------------------ | ------------------------ |
|
|
124
|
+
| Left | Menu | `Button` (icon-only) with `Icon` `menu` | Toggles Left Panel |
|
|
125
|
+
| Left | Service Name | Text | Current service name |
|
|
126
|
+
| Center | Breadcrumbs | `BreadcrumbGroup` | Single breadcrumb source |
|
|
127
|
+
| Right | Panel Toggle | `Button` (icon-only) with `Icon` `view-vertical` | Toggles Right Panel |
|
|
128
|
+
|
|
129
|
+
### Left Panel
|
|
130
|
+
|
|
131
|
+
- Slot: `navigation` in `AppLayoutToolbar`
|
|
132
|
+
- Component: `SideNavigation`
|
|
133
|
+
- Header: `Header` with an icon-only `Button` (`Icon` `angle-left`) to collapse
|
|
134
|
+
- Content: Empty placeholder list (service provides content)
|
|
135
|
+
|
|
136
|
+
### Right Panel
|
|
137
|
+
|
|
138
|
+
- Slot: `tools` in `AppLayoutToolbar`
|
|
139
|
+
- Content: Empty container (no `HelpPanel`)
|
|
140
|
+
|
|
141
|
+
### Notifications
|
|
142
|
+
|
|
143
|
+
- Slot: `notifications` in `AppLayoutToolbar`
|
|
144
|
+
- Component: `Flashbar`
|
|
145
|
+
|
|
146
|
+
### Main Content Area
|
|
147
|
+
|
|
148
|
+
- Renders `children` passed to `AppShell`
|
|
149
|
+
|
|
150
|
+
## Required Cloudscape Packages
|
|
151
|
+
|
|
152
|
+
- `@cloudscape-design/components`
|
|
153
|
+
- `@cloudscape-design/global-styles` (import once per app for base styles/fonts)
|
|
154
|
+
|
|
155
|
+
## Service Catalog Dependency
|
|
156
|
+
|
|
157
|
+
The App Shell builds the **All services** menu and global search suggestions from
|
|
158
|
+
`@mostrom/service-catalog`. Until we move to a monorepo workspace setup, each
|
|
159
|
+
app should explicitly depend on this package to avoid module resolution issues.
|
|
160
|
+
|
|
161
|
+
Add this to your app's `package.json` dependencies:
|
|
162
|
+
|
|
163
|
+
```json
|
|
164
|
+
{
|
|
165
|
+
"@mostrom/service-catalog": "file:../../../shared/packages/service-catalog"
|
|
166
|
+
}
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
## Notes
|
|
170
|
+
|
|
171
|
+
- Do not duplicate breadcrumbs outside the `BreadcrumbGroup`.
|
|
172
|
+
- Use Cloudscape components as-is. No custom styling overrides required for the initial scaffold.
|
package/bin/init.js
ADDED
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @platform/app-shell init
|
|
5
|
+
*
|
|
6
|
+
* Sets up a new app to use the app-shell package correctly.
|
|
7
|
+
* Similar to `shadcn init` but configured for our monorepo structure.
|
|
8
|
+
*
|
|
9
|
+
* Usage (from your app's frontend directory):
|
|
10
|
+
* node ../../../shared/packages/app-shell/bin/init.js
|
|
11
|
+
* node ../../../shared/packages/app-shell/bin/init.js --force
|
|
12
|
+
*
|
|
13
|
+
* Note: npx/bunx won't work - this is a private monorepo package, not published to npm.
|
|
14
|
+
*
|
|
15
|
+
* What it does:
|
|
16
|
+
* 1. Creates minimal app/tailwind.css (delegates to app-shell)
|
|
17
|
+
* 2. Creates components.json (shadcn/ui configuration)
|
|
18
|
+
* 3. Creates app/lib/utils.ts (cn utility + getBasePathHref)
|
|
19
|
+
* 4. Creates tailwind.config.ts
|
|
20
|
+
* 5. Validates vite.config.ts uses getSharedViteConfig()
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
import fs from "node:fs";
|
|
24
|
+
import path from "node:path";
|
|
25
|
+
import { fileURLToPath } from "node:url";
|
|
26
|
+
|
|
27
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
28
|
+
const __dirname = path.dirname(__filename);
|
|
29
|
+
const cwd = process.cwd();
|
|
30
|
+
|
|
31
|
+
// ANSI colors
|
|
32
|
+
const green = (s) => `\x1b[32m${s}\x1b[0m`;
|
|
33
|
+
const yellow = (s) => `\x1b[33m${s}\x1b[0m`;
|
|
34
|
+
const red = (s) => `\x1b[31m${s}\x1b[0m`;
|
|
35
|
+
const cyan = (s) => `\x1b[36m${s}\x1b[0m`;
|
|
36
|
+
const dim = (s) => `\x1b[2m${s}\x1b[0m`;
|
|
37
|
+
|
|
38
|
+
console.log();
|
|
39
|
+
console.log(cyan("@platform/app-shell init"));
|
|
40
|
+
console.log(dim("Setting up app-shell configuration..."));
|
|
41
|
+
console.log();
|
|
42
|
+
|
|
43
|
+
// Templates
|
|
44
|
+
const TAILWIND_CSS = `@import "https://fonts.googleapis.com/css2?family=Hanken+Grotesk:wght@300;400;500;600;700&display=swap";
|
|
45
|
+
@import "tw-animate-css";
|
|
46
|
+
@import "tailwindcss";
|
|
47
|
+
@import "@platform/app-shell/styles.css";
|
|
48
|
+
@config "../tailwind.config.ts";
|
|
49
|
+
`;
|
|
50
|
+
|
|
51
|
+
// Compute relative path to app-shell from current directory
|
|
52
|
+
function getAppShellRelativePath() {
|
|
53
|
+
// The init script is at app-shell/bin/init.js
|
|
54
|
+
// So app-shell src is at ../src relative to this script
|
|
55
|
+
const appShellSrc = path.resolve(__dirname, "../src");
|
|
56
|
+
const relativePath = path.relative(cwd, appShellSrc);
|
|
57
|
+
return relativePath.replace(/\\/g, "/"); // Normalize for all platforms
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const appShellPath = getAppShellRelativePath();
|
|
61
|
+
|
|
62
|
+
const TAILWIND_CONFIG = `export default {
|
|
63
|
+
darkMode: "class",
|
|
64
|
+
content: [
|
|
65
|
+
"./app/**/*.{ts,tsx,js,jsx}",
|
|
66
|
+
// Include app-shell components for Tailwind to scan
|
|
67
|
+
"${appShellPath}/**/*.{ts,tsx}",
|
|
68
|
+
],
|
|
69
|
+
};
|
|
70
|
+
`;
|
|
71
|
+
|
|
72
|
+
const COMPONENTS_JSON = `{
|
|
73
|
+
"$schema": "https://ui.shadcn.com/schema.json",
|
|
74
|
+
"style": "new-york",
|
|
75
|
+
"rsc": false,
|
|
76
|
+
"tsx": true,
|
|
77
|
+
"tailwind": {
|
|
78
|
+
"config": "tailwind.config.ts",
|
|
79
|
+
"css": "app/tailwind.css",
|
|
80
|
+
"baseColor": "neutral",
|
|
81
|
+
"cssVariables": true,
|
|
82
|
+
"prefix": ""
|
|
83
|
+
},
|
|
84
|
+
"iconLibrary": "lucide",
|
|
85
|
+
"rtl": false,
|
|
86
|
+
"aliases": {
|
|
87
|
+
"components": "~/components",
|
|
88
|
+
"utils": "~/lib/utils",
|
|
89
|
+
"ui": "~/components/ui",
|
|
90
|
+
"lib": "~/lib",
|
|
91
|
+
"hooks": "~/hooks"
|
|
92
|
+
},
|
|
93
|
+
"registries": {}
|
|
94
|
+
}
|
|
95
|
+
`;
|
|
96
|
+
|
|
97
|
+
const UTILS_TS = `import { clsx, type ClassValue } from "clsx"
|
|
98
|
+
import { twMerge } from "tailwind-merge"
|
|
99
|
+
|
|
100
|
+
export function cn(...inputs: ClassValue[]) {
|
|
101
|
+
return twMerge(clsx(inputs))
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Returns a path with the VITE_BASE_PATH prefix for use in href attributes.
|
|
106
|
+
* React Router's navigate() handles basename automatically, but href attributes
|
|
107
|
+
* on Cloudscape Link components (or native <a> tags) do not.
|
|
108
|
+
*
|
|
109
|
+
* @param path - The relative path (e.g., "/clients/123" or "/scheduling/abc")
|
|
110
|
+
* @returns The path prefixed with the base path (e.g., "/client-management/clients/123")
|
|
111
|
+
*/
|
|
112
|
+
export function getBasePathHref(path: string): string {
|
|
113
|
+
const configuredBasePath = import.meta.env.VITE_BASE_PATH;
|
|
114
|
+
if (typeof configuredBasePath !== "string") {
|
|
115
|
+
throw new Error("VITE_BASE_PATH must be defined");
|
|
116
|
+
}
|
|
117
|
+
const basePath = configuredBasePath.replace(/\\/$/, "");
|
|
118
|
+
const normalizedPath = path.startsWith("/") ? path : \`/\${path}\`;
|
|
119
|
+
if (basePath === "" || basePath === "/") {
|
|
120
|
+
return normalizedPath;
|
|
121
|
+
}
|
|
122
|
+
return \`\${basePath}\${normalizedPath}\`;
|
|
123
|
+
}
|
|
124
|
+
`;
|
|
125
|
+
|
|
126
|
+
// Helper functions
|
|
127
|
+
function ensureDir(dir) {
|
|
128
|
+
if (!fs.existsSync(dir)) {
|
|
129
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function writeFile(filePath, content, overwrite = false) {
|
|
134
|
+
const relativePath = path.relative(cwd, filePath);
|
|
135
|
+
|
|
136
|
+
if (fs.existsSync(filePath) && !overwrite) {
|
|
137
|
+
// Check if content is different
|
|
138
|
+
const existing = fs.readFileSync(filePath, "utf-8");
|
|
139
|
+
if (existing === content) {
|
|
140
|
+
console.log(dim(` ${relativePath} (unchanged)`));
|
|
141
|
+
return false;
|
|
142
|
+
}
|
|
143
|
+
console.log(yellow(` ${relativePath} (exists, skipping - use --force to overwrite)`));
|
|
144
|
+
return false;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
fs.writeFileSync(filePath, content);
|
|
148
|
+
console.log(green(` ${relativePath}`));
|
|
149
|
+
return true;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function checkViteConfig() {
|
|
153
|
+
const viteConfigPath = path.join(cwd, "vite.config.ts");
|
|
154
|
+
if (!fs.existsSync(viteConfigPath)) {
|
|
155
|
+
console.log(yellow(" vite.config.ts not found - skipping validation"));
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const content = fs.readFileSync(viteConfigPath, "utf-8");
|
|
160
|
+
|
|
161
|
+
if (!content.includes("getSharedViteConfig")) {
|
|
162
|
+
console.log();
|
|
163
|
+
console.log(yellow("Warning: vite.config.ts does not use getSharedViteConfig()"));
|
|
164
|
+
console.log(dim("Consider updating it to use the shared config:"));
|
|
165
|
+
console.log();
|
|
166
|
+
console.log(dim(` import { getSharedViteConfig } from "@platform/app-shell/vite";`));
|
|
167
|
+
console.log(dim(` import { mergeConfig } from "vite";`));
|
|
168
|
+
console.log();
|
|
169
|
+
console.log(dim(` export default defineConfig(({ mode }) => {`));
|
|
170
|
+
console.log(dim(` const shared = getSharedViteConfig(__dirname);`));
|
|
171
|
+
console.log(dim(` return mergeConfig(shared, { /* app-specific config */ });`));
|
|
172
|
+
console.log(dim(` });`));
|
|
173
|
+
} else {
|
|
174
|
+
console.log(green(" vite.config.ts uses getSharedViteConfig()"));
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function checkConflictingCss() {
|
|
179
|
+
// Check for app.css or other CSS files that might conflict with app-shell
|
|
180
|
+
const conflictingFiles = ["app/app.css", "app/global.css", "app/globals.css"];
|
|
181
|
+
const found = [];
|
|
182
|
+
|
|
183
|
+
for (const file of conflictingFiles) {
|
|
184
|
+
const filePath = path.join(cwd, file);
|
|
185
|
+
if (fs.existsSync(filePath)) {
|
|
186
|
+
const content = fs.readFileSync(filePath, "utf-8");
|
|
187
|
+
// Check if it has html/body/root blocks with hardcoded colors
|
|
188
|
+
// These patterns specifically target global style overrides
|
|
189
|
+
const hasGlobalColorOverride =
|
|
190
|
+
/(?:html|body|\:root)\s*\{[^}]*(?:background-color|background|color)\s*:/i.test(content);
|
|
191
|
+
|
|
192
|
+
if (hasGlobalColorOverride) {
|
|
193
|
+
found.push(file);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
if (found.length > 0) {
|
|
199
|
+
console.log();
|
|
200
|
+
console.log(yellow("Warning: Found CSS files that may conflict with app-shell:"));
|
|
201
|
+
for (const file of found) {
|
|
202
|
+
console.log(red(` ${file}`));
|
|
203
|
+
}
|
|
204
|
+
console.log(dim("These files have html/body/:root blocks with hardcoded colors."));
|
|
205
|
+
console.log(dim("Remove these blocks and rely on app-shell/styles.css for theming."));
|
|
206
|
+
} else {
|
|
207
|
+
console.log(green(" No conflicting CSS files found"));
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// Check for --force flag
|
|
212
|
+
const force = process.argv.includes("--force") || process.argv.includes("-f");
|
|
213
|
+
|
|
214
|
+
// Preflight checks
|
|
215
|
+
console.log("Preflight checks:");
|
|
216
|
+
|
|
217
|
+
// Check if we're in a frontend directory
|
|
218
|
+
const packageJsonPath = path.join(cwd, "package.json");
|
|
219
|
+
if (!fs.existsSync(packageJsonPath)) {
|
|
220
|
+
console.log(red(" package.json not found"));
|
|
221
|
+
console.log(dim(" Run this command from your frontend directory"));
|
|
222
|
+
process.exit(1);
|
|
223
|
+
}
|
|
224
|
+
console.log(green(" Found package.json"));
|
|
225
|
+
|
|
226
|
+
// Check for app directory (React Router convention)
|
|
227
|
+
const appDir = path.join(cwd, "app");
|
|
228
|
+
if (!fs.existsSync(appDir)) {
|
|
229
|
+
console.log(yellow(" app/ directory not found - will create it"));
|
|
230
|
+
}
|
|
231
|
+
console.log(green(" Found app/ directory"));
|
|
232
|
+
|
|
233
|
+
// Check for @platform/app-shell dependency
|
|
234
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
|
|
235
|
+
const hasAppShell = packageJson.dependencies?.["@platform/app-shell"] ||
|
|
236
|
+
packageJson.devDependencies?.["@platform/app-shell"];
|
|
237
|
+
if (!hasAppShell) {
|
|
238
|
+
console.log(yellow(" @platform/app-shell not in dependencies"));
|
|
239
|
+
console.log(dim(" Add it: \"@platform/app-shell\": \"file:../../../shared/packages/app-shell\""));
|
|
240
|
+
} else {
|
|
241
|
+
console.log(green(" Found @platform/app-shell dependency"));
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
console.log();
|
|
245
|
+
console.log("Creating files:");
|
|
246
|
+
|
|
247
|
+
// Ensure directories exist
|
|
248
|
+
ensureDir(path.join(cwd, "app"));
|
|
249
|
+
ensureDir(path.join(cwd, "app/lib"));
|
|
250
|
+
|
|
251
|
+
// Create files
|
|
252
|
+
writeFile(path.join(cwd, "app/tailwind.css"), TAILWIND_CSS, force);
|
|
253
|
+
writeFile(path.join(cwd, "tailwind.config.ts"), TAILWIND_CONFIG, force);
|
|
254
|
+
writeFile(path.join(cwd, "components.json"), COMPONENTS_JSON, force);
|
|
255
|
+
writeFile(path.join(cwd, "app/lib/utils.ts"), UTILS_TS, force);
|
|
256
|
+
|
|
257
|
+
console.log();
|
|
258
|
+
console.log("Validating configuration:");
|
|
259
|
+
checkViteConfig();
|
|
260
|
+
checkConflictingCss();
|
|
261
|
+
|
|
262
|
+
console.log();
|
|
263
|
+
console.log(green("Done!"));
|
|
264
|
+
console.log();
|
|
265
|
+
console.log("Next steps:");
|
|
266
|
+
console.log(dim(" 1. Run 'bun install' to install dependencies"));
|
|
267
|
+
console.log(dim(" 2. Import '@platform/app-shell' in your layout component"));
|
|
268
|
+
console.log(dim(" 3. Run 'bun run dev' to start the dev server"));
|
|
269
|
+
console.log();
|