@live-change/frontend-template 0.9.197 → 0.9.199

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/.claude/rules/live-change-backend-actions-views-triggers.md +184 -0
  2. package/.claude/rules/live-change-backend-architecture.md +126 -0
  3. package/.claude/rules/live-change-backend-models-and-relations.md +188 -0
  4. package/.claude/rules/live-change-frontend-vue-primevue.md +291 -0
  5. package/.claude/rules/live-change-service-structure.md +89 -0
  6. package/.claude/skills/create-skills-and-rules.md +196 -0
  7. package/.claude/skills/live-change-design-actions-views-triggers.md +190 -0
  8. package/.claude/skills/live-change-design-models-relations.md +173 -0
  9. package/.claude/skills/live-change-design-service.md +132 -0
  10. package/.claude/skills/live-change-frontend-action-buttons.md +128 -0
  11. package/.claude/skills/live-change-frontend-action-form.md +143 -0
  12. package/.claude/skills/live-change-frontend-analytics.md +146 -0
  13. package/.claude/skills/live-change-frontend-command-forms.md +215 -0
  14. package/.claude/skills/live-change-frontend-data-views.md +182 -0
  15. package/.claude/skills/live-change-frontend-editor-form.md +177 -0
  16. package/.claude/skills/live-change-frontend-locale-time.md +171 -0
  17. package/.claude/skills/live-change-frontend-page-list-detail.md +200 -0
  18. package/.claude/skills/live-change-frontend-range-list.md +128 -0
  19. package/.claude/skills/live-change-frontend-ssr-setup.md +118 -0
  20. package/.cursor/rules/live-change-backend-actions-views-triggers.mdc +202 -0
  21. package/.cursor/rules/live-change-backend-architecture.mdc +131 -0
  22. package/.cursor/rules/live-change-backend-models-and-relations.mdc +194 -0
  23. package/.cursor/rules/live-change-frontend-vue-primevue.mdc +290 -0
  24. package/.cursor/rules/live-change-service-structure.mdc +107 -0
  25. package/.cursor/skills/live-change-design-actions-views-triggers.md +197 -0
  26. package/.cursor/skills/live-change-design-models-relations.md +168 -0
  27. package/.cursor/skills/live-change-design-service.md +75 -0
  28. package/.cursor/skills/live-change-frontend-action-buttons.md +128 -0
  29. package/.cursor/skills/live-change-frontend-action-form.md +143 -0
  30. package/.cursor/skills/live-change-frontend-analytics.md +146 -0
  31. package/.cursor/skills/live-change-frontend-command-forms.md +215 -0
  32. package/.cursor/skills/live-change-frontend-data-views.md +182 -0
  33. package/.cursor/skills/live-change-frontend-editor-form.md +177 -0
  34. package/.cursor/skills/live-change-frontend-locale-time.md +171 -0
  35. package/.cursor/skills/live-change-frontend-page-list-detail.md +200 -0
  36. package/.cursor/skills/live-change-frontend-range-list.md +128 -0
  37. package/.cursor/skills/live-change-frontend-ssr-setup.md +119 -0
  38. package/README.md +71 -0
  39. package/package.json +50 -50
  40. package/server/app.config.js +35 -0
  41. package/server/services.list.js +2 -0
  42. package/.nx/workspace-data/file-map.json +0 -195
  43. package/.nx/workspace-data/nx_files.nxt +0 -0
  44. package/.nx/workspace-data/project-graph.json +0 -8
  45. package/.nx/workspace-data/project-graph.lock +0 -0
  46. package/.nx/workspace-data/source-maps.json +0 -1
@@ -0,0 +1,171 @@
1
+ ---
2
+ description: Handle locale, timezone, currentTime, time synchronization and email locale
3
+ ---
4
+
5
+ # Skill: live-change-frontend-locale-time (Claude Code)
6
+
7
+ Use this skill when you work with **locale, language, time, and timezone** in a LiveChange frontend.
8
+
9
+ ## When to use
10
+
11
+ - You need to display dates/times in the user's timezone.
12
+ - You are building a time-sensitive feature (countdown, deadline, scheduling).
13
+ - You need to sync locale with vue-i18n.
14
+ - You are writing an email template that must use the recipient's language.
15
+
16
+ ## Step 1 – Set up locale in App.vue
17
+
18
+ ```javascript
19
+ import { watch } from 'vue'
20
+ import { useI18n } from 'vue-i18n'
21
+ import { useLocale } from '@live-change/vue3-components'
22
+
23
+ const { locale: i18nLocale } = useI18n()
24
+ const locale = useLocale()
25
+
26
+ // Capture browser locale settings and save to backend
27
+ locale.captureLocale()
28
+
29
+ // Watch for locale changes and sync with vue-i18n
30
+ locale.getLocaleObservable()
31
+ watch(() => locale.localeRef.value, (newLocale) => {
32
+ if (newLocale?.language && i18nLocale.value !== newLocale.language) {
33
+ i18nLocale.value = newLocale.language
34
+ }
35
+ }, { immediate: true })
36
+ ```
37
+
38
+ ## Step 2 – Display dates in user timezone
39
+
40
+ Use vue-i18n's `d()` function for formatting and `locale.localTime()` for SSR timezone conversion:
41
+
42
+ ```vue
43
+ <template>
44
+ <span>{{ d(locale.localTime(new Date(article.createdAt)), 'long') }}</span>
45
+ </template>
46
+
47
+ <script setup>
48
+ import { useI18n } from 'vue-i18n'
49
+ import { useLocale } from '@live-change/vue3-components'
50
+
51
+ const { d } = useI18n()
52
+ const locale = useLocale()
53
+ </script>
54
+ ```
55
+
56
+ `locale.localTime(date)` converts server timestamps to the user's timezone during SSR. On the client (browser), it returns the date unchanged since the browser handles timezone natively.
57
+
58
+ ## Step 3 – Use `currentTime` for reactive clocks
59
+
60
+ `currentTime` is a global `Ref<number>` that ticks every 500ms:
61
+
62
+ ```vue
63
+ <template>
64
+ <span>{{ formattedTime }}</span>
65
+ </template>
66
+
67
+ <script setup>
68
+ import { computed } from 'vue'
69
+ import { currentTime } from '@live-change/frontend-base'
70
+ import { useI18n } from 'vue-i18n'
71
+
72
+ const { d } = useI18n()
73
+ const formattedTime = computed(() =>
74
+ isNaN(currentTime.value) ? '' : d(new Date(currentTime.value), 'time')
75
+ )
76
+ </script>
77
+ ```
78
+
79
+ Any component reading `currentTime` re-renders automatically every 500ms.
80
+
81
+ ## Step 4 – Correct clock skew with `useTimeSynchronization`
82
+
83
+ When the client clock differs from the server clock (important for real-time features like quizzes, countdowns, auctions):
84
+
85
+ ```javascript
86
+ import { useTimeSynchronization } from '@live-change/vue3-ssr'
87
+ import { currentTime } from '@live-change/frontend-base'
88
+
89
+ const timeSync = useTimeSynchronization()
90
+
91
+ // Convert client time to server time (reactive computed)
92
+ const serverTime = timeSync.localToServerComputed(currentTime)
93
+
94
+ // Countdown to a server-side deadline
95
+ const timeRemaining = computed(() => deadline.value - serverTime.value)
96
+ const seconds = computed(() => Math.max(0, Math.floor(timeRemaining.value / 1000) % 60))
97
+ const minutes = computed(() => Math.max(0, Math.floor(timeRemaining.value / 1000 / 60)))
98
+ ```
99
+
100
+ Enable time synchronization in `config.js`:
101
+
102
+ ```javascript
103
+ export default {
104
+ timeSynchronization: true,
105
+ // ...
106
+ }
107
+ ```
108
+
109
+ **When to use:** Only when clock skew matters – real-time events, countdown timers, time-limited actions. For simple date display, `currentTime` alone is sufficient.
110
+
111
+ ### Returned object from `useTimeSynchronization()`:
112
+
113
+ | Property | Description |
114
+ |---|---|
115
+ | `diff` | Server-client time offset in ms |
116
+ | `synchronized` | `true` once sync is complete |
117
+ | `serverToLocal(ts)` | Convert server timestamp to local |
118
+ | `localToServer(ts)` | Convert local timestamp to server |
119
+ | `serverToLocalComputed(ts)` | Reactive computed version |
120
+ | `localToServerComputed(ts)` | Reactive computed version |
121
+
122
+ ## Step 5 – Smooth countdown with requestAnimationFrame
123
+
124
+ For sub-500ms precision (e.g. animated countdown knobs):
125
+
126
+ ```javascript
127
+ import { ref } from 'vue'
128
+ import { useRafFn } from '@vueuse/core'
129
+ import { useTimeSynchronization } from '@live-change/vue3-ssr'
130
+
131
+ const rafNow = ref(Date.now())
132
+ useRafFn(() => { rafNow.value = Date.now() })
133
+
134
+ const timeSync = useTimeSynchronization()
135
+ const rafServerTime = timeSync.localToServerComputed(rafNow)
136
+
137
+ const countdown = computed(() =>
138
+ Math.max(0, deadline.value - rafServerTime.value)
139
+ )
140
+ ```
141
+
142
+ ## Step 6 – Locale in email templates
143
+
144
+ Email templates render server-side. They fetch the recipient's locale explicitly:
145
+
146
+ ```javascript
147
+ import { useI18n } from 'vue-i18n'
148
+ import { useLocale } from '@live-change/vue3-components'
149
+
150
+ const { locale: i18nLocale, t } = useI18n()
151
+ const locale = useLocale()
152
+
153
+ // props.json contains { user, client: { session } }
154
+ const data = JSON.parse(json)
155
+
156
+ // Fetch locale for the recipient (not current session)
157
+ await Promise.all([
158
+ locale.getOtherUserOrSessionLocale(data.user, data.client?.session)
159
+ ])
160
+
161
+ // Apply to vue-i18n
162
+ if (locale.getLanguage()) i18nLocale.value = locale.getLanguage()
163
+ ```
164
+
165
+ Key differences from regular pages:
166
+
167
+ | Aspect | Regular page | Email template |
168
+ |---|---|---|
169
+ | Locale source | `locale.getLocale()` (current user) | `locale.getOtherUserOrSessionLocale(user, session)` |
170
+ | Time conversion | `locale.localTime()` only on SSR | Always server-side |
171
+ | Reactive updates | Yes | No (one-shot SSR render) |
@@ -0,0 +1,200 @@
1
+ ---
2
+ description: Build list and detail pages with live data, computed paths, .with() and useClient
3
+ ---
4
+
5
+ # Skill: live-change-frontend-page-list-detail (Claude Code)
6
+
7
+ Use this skill when you need to build a **list + detail** UI in Vue 3 / PrimeVue / Tailwind for a LiveChange backend.
8
+
9
+ ## When to use
10
+
11
+ - You are adding a new list page (devices, orders, etc.).
12
+ - You are adding a detail page for a single object.
13
+ - You want to follow the `live(path)` + `Promise.all` pattern compatible with SSR.
14
+
15
+ ## Step 1 – List page (`src/pages/<resource>/index.vue`)
16
+
17
+ 1. Create the file: `src/pages/<resource>/index.vue`.
18
+ 2. In the `<template>`, follow this layout:
19
+ - container with padding (`container mx-auto p-4`),
20
+ - header with title and “add” button,
21
+ - empty-state card,
22
+ - grid of cards or rows.
23
+
24
+ Example:
25
+
26
+ ```vue
27
+ <template>
28
+ <div class="container mx-auto p-4">
29
+ <div class="flex items-center justify-between mb-6">
30
+ <h1 class="text-2xl font-bold">Devices</h1>
31
+ <Button label="Add" icon="pi pi-plus" @click="openDialog" />
32
+ </div>
33
+
34
+ <Card v-if="devices.value?.length === 0">
35
+ <template #content>
36
+ <p class="text-center text-gray-500">
37
+ No devices yet
38
+ </p>
39
+ </template>
40
+ </Card>
41
+
42
+ <div class="grid gap-4">
43
+ <Card v-for="device in devices.value" :key="device.id">
44
+ <template #content>
45
+ <!-- device content -->
46
+ </template>
47
+ </Card>
48
+ </div>
49
+ </div>
50
+ </template>
51
+
52
+ <script setup>
53
+ import { path, live, api as useApi } from '@live-change/vue3-ssr'
54
+ import Button from 'primevue/button'
55
+ import Card from 'primevue/card'
56
+
57
+ const api = useApi()
58
+
59
+ const [devices] = await Promise.all([
60
+ live(path().deviceManager.myUserDevices({}))
61
+ ])
62
+
63
+ function openDialog() {
64
+ // open dialog for creating a new device
65
+ }
66
+ </script>
67
+
68
+ <route>
69
+ { "name": "devices", "meta": { "signedIn": true } }
70
+ </route>
71
+ ```
72
+
73
+ ## Step 2 – Detail page (`src/pages/<resource>/[id].vue`)
74
+
75
+ 1. Create `src/pages/<resource>/[id].vue`.
76
+ 2. In `script setup`:
77
+ - use `useRoute()` to get the id (`route.params.id`),
78
+ - fetch the main object and related data using `Promise.all`.
79
+
80
+ Example skeleton:
81
+
82
+ ```vue
83
+ <template>
84
+ <div class="container mx-auto p-4 space-y-4" v-if="item.value">
85
+ <Card>
86
+ <template #title>
87
+ {{ item.value.name }}
88
+ </template>
89
+ <template #content>
90
+ <!-- details -->
91
+ </template>
92
+ </Card>
93
+ </div>
94
+ </template>
95
+
96
+ <script setup>
97
+ import { path, live } from '@live-change/vue3-ssr'
98
+ import { useRoute } from 'vue-router'
99
+ import Card from 'primevue/card'
100
+
101
+ const route = useRoute()
102
+ const id = route.params.id
103
+
104
+ const [item] = await Promise.all([
105
+ live(path().myService.myUserItem({ item: id }))
106
+ ])
107
+ </script>
108
+
109
+ <route>
110
+ { "name": "myItem", "meta": { "signedIn": true } }
111
+ </route>
112
+ ```
113
+
114
+ ## Step 3 – Computed paths with reactive parameters
115
+
116
+ When the path depends on reactive values (route params, props), wrap it in `computed()`:
117
+
118
+ ```js
119
+ import { computed, unref } from 'vue'
120
+ import { usePath, live } from '@live-change/vue3-ssr'
121
+ import { useRoute } from 'vue-router'
122
+
123
+ const path = usePath()
124
+ const route = useRoute()
125
+ const deviceId = route.params.device
126
+
127
+ const devicePath = computed(() => path.deviceManager.myUserDevice({ device: unref(deviceId) }))
128
+ const connectionsPath = computed(() =>
129
+ path.deviceManager.deviceOwnedDeviceConnections({ device: unref(deviceId) })
130
+ )
131
+
132
+ const [device, connections] = await Promise.all([
133
+ live(devicePath),
134
+ live(connectionsPath),
135
+ ])
136
+ ```
137
+
138
+ ## Step 4 – Attach related objects with `.with()`
139
+
140
+ Use `.with()` to load related data alongside each item:
141
+
142
+ ```js
143
+ const devicesPath = computed(() =>
144
+ path.deviceManager.myUserDevices({})
145
+ .with(device => path.deviceManager.deviceState({ device: device.id }).bind('state'))
146
+ .with(device => path.userIdentification.identification({
147
+ sessionOrUserType: device.ownerType,
148
+ sessionOrUser: device.owner
149
+ }).bind('ownerProfile'))
150
+ )
151
+
152
+ const [devices] = await Promise.all([live(devicesPath)])
153
+ ```
154
+
155
+ Access in template: `device.state?.online`, `device.ownerProfile?.firstName`.
156
+
157
+ ## Step 5 – Auth guard with `useClient`
158
+
159
+ Use `useClient()` to conditionally show UI or load data based on authentication:
160
+
161
+ ```js
162
+ import { useClient } from '@live-change/vue3-ssr'
163
+
164
+ const client = useClient()
165
+
166
+ // Conditional path (null = no fetch)
167
+ const adminDataPath = computed(() =>
168
+ client.value.roles.includes('admin') && path.deviceManager.allDevices({})
169
+ )
170
+ const [adminData] = await Promise.all([live(adminDataPath)])
171
+ ```
172
+
173
+ Template:
174
+
175
+ ```vue
176
+ <Button v-if="client.roles.includes('admin')" label="Admin panel" />
177
+ <div v-if="!client.user">
178
+ <p>Please sign in</p>
179
+ <router-link :to="{ name: 'user:signIn' }">Sign in</router-link>
180
+ </div>
181
+ ```
182
+
183
+ ## Step 6 – Status tags and simple indicators
184
+
185
+ 1. Use PrimeVue `Tag` for status fields.
186
+ 2. Map statuses to severities (`success`, `secondary`, etc.).
187
+
188
+ Example:
189
+
190
+ ```vue
191
+ <Tag
192
+ :value="conn.status"
193
+ :severity="conn.status === 'online' ? 'success' : 'secondary'"
194
+ />
195
+ ```
196
+
197
+ ## Step 7 – Large lists with RangeViewer
198
+
199
+ For large or infinite-scroll lists, use `<RangeViewer>` instead of loading everything at once. See the `live-change-frontend-range-list` skill for details.
200
+
@@ -0,0 +1,128 @@
1
+ ---
2
+ description: Build paginated scrollable lists with RangeViewer, rangeBuckets and .with()
3
+ ---
4
+
5
+ # Skill: live-change-frontend-range-list (Claude Code)
6
+
7
+ Use this skill when you build **paginated, scrollable lists** backed by DAO ranges in a LiveChange frontend.
8
+
9
+ ## When to use
10
+
11
+ - You need a list that loads items in pages (infinite scroll).
12
+ - The list is backed by a DAO range view (e.g. `articlesByCreatedAt`).
13
+ - You want to attach related objects to each item via `.with()`.
14
+
15
+ ## Step 1 – Define the path function
16
+
17
+ The path function receives a `range` object (with `gt`, `gte`, `lt`, `lte`, `limit`, `reverse`) and returns a DAO path.
18
+
19
+ Use `reverseRange()` to display items newest-first:
20
+
21
+ ```javascript
22
+ import { reverseRange } from '@live-change/vue3-ssr'
23
+
24
+ function articlesPathRange(range) {
25
+ return path.blog.articlesByCreatedAt({ ...reverseRange(range) })
26
+ }
27
+ ```
28
+
29
+ ## Step 2 – Attach related objects with `.with()`
30
+
31
+ Chain `.with()` calls to load related data for each item:
32
+
33
+ ```javascript
34
+ function articlesPathRange(range) {
35
+ return path.blog.articlesByCreatedAt({ ...reverseRange(range) })
36
+ .with(article => path.userIdentification.identification({
37
+ sessionOrUserType: article.authorType,
38
+ sessionOrUser: article.author
39
+ }).bind('authorProfile'))
40
+ .with(article => path.blog.articleStats({ article: article.id }).bind('stats'))
41
+ }
42
+ ```
43
+
44
+ Each `.with()` call:
45
+ - receives a proxy of the item,
46
+ - builds a path to the related data,
47
+ - calls `.bind('fieldName')` to attach the result under that field name.
48
+
49
+ Nested `.with()` is also supported:
50
+
51
+ ```javascript
52
+ function eventsPathRange(range) {
53
+ return path.myService.allEvents({ ...reverseRange(range) })
54
+ .with(event => path.myService.eventState({ event: event.id }).bind('state')
55
+ .with(state => path.myService.roundPairs({ event: event.id, round: state.round }).bind('roundPairs'))
56
+ )
57
+ }
58
+ ```
59
+
60
+ ## Step 3 – Use `<RangeViewer>` in the template
61
+
62
+ ```vue
63
+ <template>
64
+ <RangeViewer
65
+ :pathFunction="articlesPathRange"
66
+ :canLoadTop="false"
67
+ canDropBottom
68
+ loadBottomSensorSize="3000px"
69
+ dropBottomSensorSize="5000px"
70
+ >
71
+ <template #empty>
72
+ <p class="text-center text-surface-500 my-4">No articles yet.</p>
73
+ </template>
74
+ <template #default="{ item: article }">
75
+ <Card class="mb-2">
76
+ <template #content>
77
+ <h3>{{ article.title }}</h3>
78
+ <p class="text-sm text-surface-500">By {{ article.authorProfile?.firstName }}</p>
79
+ </template>
80
+ </Card>
81
+ </template>
82
+ </RangeViewer>
83
+ </template>
84
+ ```
85
+
86
+ Key props:
87
+
88
+ | Prop | Default | Description |
89
+ |---|---|---|
90
+ | `pathFunction` | required | `(range) => path` – builds the DAO path for a given range |
91
+ | `bucketSize` | `20` | Items per page |
92
+ | `canLoadTop` / `canLoadBottom` | `true` | Whether loading in each direction is allowed |
93
+ | `canDropTop` / `canDropBottom` | `false` | Whether to drop pages that scrolled far out of view |
94
+ | `loadBottomSensorSize` | `'500px'` | How far before the bottom to trigger loading (increase for smoother UX) |
95
+ | `dropBottomSensorSize` | `'5000px'` | How far to keep before dropping |
96
+ | `frozen` | `false` | Pause live updates |
97
+
98
+ Slots:
99
+
100
+ | Slot | Props | Description |
101
+ |---|---|---|
102
+ | `default` | `{ item, bucket, itemIndex, bucketIndex }` | Render each item |
103
+ | `empty` | – | Shown when there are no items |
104
+ | `loadingTop` / `loadingBottom` | – | Loading spinners |
105
+ | `changedTop` / `changedBottom` | – | Indicators when data changed while frozen |
106
+
107
+ ## Step 4 – Low-level `rangeBuckets` (optional)
108
+
109
+ For advanced control, use `rangeBuckets` directly:
110
+
111
+ ```javascript
112
+ import { rangeBuckets, reverseRange } from '@live-change/vue3-ssr'
113
+
114
+ const { buckets, loadBottom, dropTop, freeze, unfreeze } = await rangeBuckets(
115
+ (range) => path.blog.articlesByCreatedAt({ ...reverseRange(range) }),
116
+ { bucketSize: 20 }
117
+ )
118
+ ```
119
+
120
+ Iterate in the template:
121
+
122
+ ```vue
123
+ <template v-for="(bucket, bi) in buckets.value" :key="bi">
124
+ <div v-for="(item, ii) in bucket.data.value" :key="item.id">
125
+ <!-- render item -->
126
+ </div>
127
+ </template>
128
+ ```
@@ -0,0 +1,119 @@
1
+ ---
2
+ description: Set up SSR entry points, router, PrimeVue theme and Suspense data loading
3
+ ---
4
+
5
+ # Skill: live-change-frontend-ssr-setup
6
+
7
+ Ten skill opisuje **konfigurację frontendu** opartego o live-change-stack:
8
+
9
+ - entry-client / entry-server,
10
+ - router i meta `signedIn`,
11
+ - konfigurację PrimeVue / motywu.
12
+
13
+ ## 1. Entry points – klient i serwer
14
+
15
+ 1. W projekcie frontendowym upewnij się, że masz dwa entry points:
16
+ - `entry-client.js` (lub `.ts`),
17
+ - `entry-server.js` (lub `.ts`).
18
+
19
+ 2. Skorzystaj z helperów z `@live-change/frontend-base`:
20
+
21
+ ```js
22
+ // entry-client.js
23
+ import { clientEntry } from '@live-change/frontend-base/client-entry.js'
24
+ import App from './App.vue'
25
+ import { createRouter } from './router.js'
26
+ import { config } from './config.js'
27
+
28
+ export default clientEntry(App, createRouter, config)
29
+ ```
30
+
31
+ ```js
32
+ // entry-server.js
33
+ import { serverEntry, sitemapEntry } from '@live-change/frontend-base/server-entry.js'
34
+ import App from './App.vue'
35
+ import { createRouter, routerSitemap } from './router.js'
36
+ import { config } from './config.js'
37
+
38
+ export const render = serverEntry(App, createRouter, config)
39
+ export const sitemap = sitemapEntry(App, createRouter, routerSitemap, config)
40
+ ```
41
+
42
+ ## 2. Router – `vite-plugin-pages` + meta `signedIn`
43
+
44
+ 1. Użyj `vite-plugin-pages` do generowania tras z `src/pages/`.
45
+ 2. Dodaj blok `<route>` w plikach stron, np.:
46
+
47
+ ```vue
48
+ <route>
49
+ { "name": "devices", "meta": { "signedIn": true } }
50
+ </route>
51
+ ```
52
+
53
+ 3. W routerze dodaj guard logowania (jeśli go nie ma):
54
+
55
+ ```js
56
+ router.beforeEach((to) => {
57
+ if(to.meta.signedIn && !isLoggedIn()) {
58
+ localStorage.setItem('redirectAfterLogin', to.fullPath)
59
+ return { name: 'user:signIn' }
60
+ }
61
+ })
62
+ ```
63
+
64
+ Funkcja `isLoggedIn()` może opierać się na stanie sesji/usera w store lub prostym sprawdzeniu tokena/ciasteczek.
65
+
66
+ ## 3. Konfiguracja PrimeVue – motyw i opcje
67
+
68
+ 1. W pliku `config.js` skonfiguruj PrimeVue:
69
+
70
+ ```js
71
+ import { definePreset } from '@primevue/themes'
72
+ import Aura from '@primevue/themes/aura'
73
+
74
+ const MyPreset = definePreset(Aura, {
75
+ semantic: {
76
+ primary: {
77
+ 50: '{indigo.50}',
78
+ 100: '{indigo.100}',
79
+ 200: '{indigo.200}',
80
+ 300: '{indigo.300}',
81
+ 400: '{indigo.400}',
82
+ 500: '{indigo.500}',
83
+ 600: '{indigo.600}',
84
+ 700: '{indigo.700}',
85
+ 800: '{indigo.800}',
86
+ 900: '{indigo.900}',
87
+ 950: '{indigo.950}'
88
+ }
89
+ }
90
+ })
91
+
92
+ export const config = {
93
+ theme: {
94
+ preset: MyPreset,
95
+ options: {
96
+ darkModeSelector: '.app-dark-mode'
97
+ }
98
+ }
99
+ }
100
+ ```
101
+
102
+ 2. Upewnij się, że App.vue używa tego configu przez globalną konfigurację PrimeVue (zgodnie z template’em live-change-stack).
103
+
104
+ ## 4. Globalne komponenty i formularze
105
+
106
+ 1. W `App.vue` zarejestruj globalne komponenty używane często w projekcie:
107
+ - auto-form components,
108
+ - globalne layouty, jeśli są.
109
+
110
+ 2. Dzięki temu na stronach używasz np. `<command-form>` bez lokalnego importu.
111
+
112
+ ## 5. SSR i `live/path` + Suspense
113
+
114
+ 1. Upewnij się, że root aplikacji (np. `ViewRoot`) opakowuje strony w `<Suspense>`.
115
+ 2. Strony powinny:
116
+ - używać `await Promise.all([live(path()....)])` w `script setup`,
117
+ - korzystać z `.value` w template,
118
+ - nie wykonywać pobierania danych w `onMounted`.
119
+
package/README.md ADDED
@@ -0,0 +1,71 @@
1
+ ---
2
+ title: @live-change/frontend-template
3
+ ---
4
+
5
+ ## @live-change/frontend-template
6
+
7
+ `@live-change/frontend-template` to **szablon kompletnej aplikacji frontendowej** Live Change:
8
+
9
+ - gotowy SSR + SPA oparty o `@live-change/frontend-base` i `@live-change/vue3-ssr`
10
+ - zintegrowane moduły: `user-frontend`, `security`, `content`, `blog`, `task`, `upload`, `url`, `video-call`, `peer-connection`, `auto-form` i inne
11
+ - standardowa struktura katalogów i skrypty `package.json`
12
+
13
+ Szablon jest punktem startowym dla nowych projektów (np. `family-tree`, `speed-dating` zostały na nim oparte).
14
+
15
+ ### Najważniejsze zależności
16
+
17
+ W `package.json` szablonu znajdziesz m.in.:
18
+
19
+ - `@live-change/frontend-base`
20
+ - `@live-change/frontend-auto-form`
21
+ - `@live-change/vue3-components`
22
+ - `@live-change/vue3-ssr`
23
+ - frontendy: `user-frontend`, `access-control-frontend`, `content-frontend`, `image-frontend`, `task-frontend`, `upload-frontend`, `url-frontend`, `wysiwyg-frontend`, `blog-frontend`, `video-call-frontend`, `peer-connection-frontend`
24
+
25
+ ### Skrypty uruchomieniowe
26
+
27
+ Typowe skrypty (uproszczony przegląd):
28
+
29
+ - `memDev` / `localDev` / `dev` – tryby deweloperskie z różną konfiguracją DB
30
+ - `ssrDev` – dev SSR
31
+ - `serveAll` / `serveAllMem` – produkcyjny SSR z API i usługami
32
+ - `apiServer`, `devApiServer`, `memApiServer` – warianty samego API
33
+ - `build`, `build:client`, `build:ssr`, `build:server`, `build:spa` – budowanie frontu i serwera
34
+ - `prerender*` – generowanie statycznych stron
35
+
36
+ Są one spójne z opisem w dokumentacji serwera (`server/01-getting-started.md`).
37
+
38
+ ### Struktura projektu na bazie szablonu
39
+
40
+ Typowy projekt zaczynający z `frontend-template` ma:
41
+
42
+ - `server/app.config.js` – lista usług i konfiguracja klienta
43
+ - `server/services.list.js` – eksport definicji usług
44
+ - `server/start.js` – start CLI (opisany w dokumentacji serwera)
45
+ - `front/src` – kod frontendu:
46
+ - `App.vue`
47
+ - `router.js`
48
+ - `config.js` (i18n, tematy, integracje)
49
+ - `pages/*` – strony routingu (`vite-plugin-pages`)
50
+ - `components/*` – komponenty wspólne
51
+
52
+ W `front/src/config.js` łączysz lokalizacje `frontend-base`, `frontend-auto-form` i innych modułów, podobnie jak w:
53
+
54
+ - `family-tree/front/src/config.js`
55
+ - `speed-dating/front/src/config.js`
56
+
57
+ ### Praca z szablonem
58
+
59
+ Typowy flow tworzenia nowej aplikacji:
60
+
61
+ 1. Tworzysz nowy pakiet oparty o `@live-change/frontend-template` (lub kopiujesz repo).
62
+ 2. Aktualizujesz `server/app.config.js` i `server/services.list.js`, aby włączyć tylko potrzebne usługi.
63
+ 3. Dostosowujesz `front/src/config.js` (temat, i18n, integracje analityki).
64
+ 4. Tworzysz nowe strony w `front/src/pages` oraz komponenty w `front/src/components`.
65
+ 5. Korzystasz z:
66
+ - `@live-change/vue3-ssr` do pracy z DAO,
67
+ - `@live-change/vue3-components` do logiki i formularzy,
68
+ - `@live-change/frontend-auto-form` do CRUD-ów.
69
+
70
+ Dokładny „manual” tego flow znajdzie się w sekcji `frontend/01-getting-started.md` dokumentacji.
71
+