@antelopejs/dms-saas 0.1.5 → 0.1.7
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 +3 -1
- package/frontend-vue/app/components/Register.vue +2 -2
- package/frontend-vue/app/components/WorkspaceSwitcherWidget.vue +454 -204
- package/frontend-vue/app/custom-pages/auth/no-workspace.vue +2 -2
- package/frontend-vue/app/middleware/workspace-suspended.global.ts +3 -0
- package/frontend-vue/app/plugins/footer-links.ts +3 -3
- package/frontend-vue/dms.frontend.ts +1 -1
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -30,7 +30,9 @@ This package lives in `packages/dms-saas` of the `AntelopeJS/dms-saas`
|
|
|
30
30
|
workspace. From that directory run `pnpm install`,
|
|
31
31
|
`pnpm --dir frontend-vue install`, `pnpm build`, `pnpm typecheck`, and
|
|
32
32
|
`pnpm test:frontend`. The frontend check generates a temporary workspace, then
|
|
33
|
-
builds client, SSR, and email bundles and runs Vue typechecking.
|
|
33
|
+
builds client, SSR, and email bundles and runs Vue typechecking. To run the
|
|
34
|
+
playground frontend during backend development, use `pnpm frontend:dev`; this
|
|
35
|
+
dispatches through the core CLI as `ajs dms dev`.
|
|
34
36
|
|
|
35
37
|
## Completing a registration that entered without a workspace
|
|
36
38
|
|
|
@@ -138,7 +138,7 @@ const isRegistrationClosed =
|
|
|
138
138
|
<i18n-t keypath="saas.register.legal_acceptance" tag="span">
|
|
139
139
|
<template #terms_and_conditions>
|
|
140
140
|
<DmsLink
|
|
141
|
-
to="/
|
|
141
|
+
to="/terms-and-conditions"
|
|
142
142
|
target="_blank"
|
|
143
143
|
class="underline"
|
|
144
144
|
>
|
|
@@ -147,7 +147,7 @@ const isRegistrationClosed =
|
|
|
147
147
|
</template>
|
|
148
148
|
<template #privacy_policy>
|
|
149
149
|
<DmsLink
|
|
150
|
-
to="/
|
|
150
|
+
to="/privacy-policy"
|
|
151
151
|
target="_blank"
|
|
152
152
|
class="underline"
|
|
153
153
|
>
|
|
@@ -1,247 +1,497 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import { computed, onMounted, ref, watch } from
|
|
2
|
+
import { computed, onMounted, ref, watch } from 'vue'
|
|
3
3
|
|
|
4
4
|
const props = defineProps<{
|
|
5
|
-
|
|
6
|
-
}>()
|
|
5
|
+
collapsed?: boolean
|
|
6
|
+
}>()
|
|
7
7
|
|
|
8
8
|
interface DmsSaasPublicRuntimeConfig {
|
|
9
|
-
|
|
9
|
+
admissionMode?: 'open' | 'invitation-only'
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
const toast = useToast()
|
|
13
|
-
const { t } = useI18n()
|
|
14
|
-
const { resolveApiError } = useApiErrorMessage()
|
|
12
|
+
const toast = useToast()
|
|
13
|
+
const { t } = useI18n()
|
|
14
|
+
const { resolveApiError } = useApiErrorMessage()
|
|
15
15
|
|
|
16
16
|
// Platform owners are not tenant members by default, so an owner can legitimately
|
|
17
17
|
// have no workspace at all — the switcher must still offer them a way in.
|
|
18
|
-
const isOwner = useIsOwner()
|
|
19
|
-
const config = useDmsRuntimeConfig()
|
|
18
|
+
const isOwner = useIsOwner()
|
|
19
|
+
const config = useDmsRuntimeConfig()
|
|
20
20
|
const canCreateWorkspace = computed(
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
)
|
|
26
|
-
|
|
27
|
-
const SEARCH_THRESHOLD = 6
|
|
28
|
-
const HOME_ROUTE =
|
|
29
|
-
|
|
30
|
-
const { workspaces, refresh } = useMyWorkspaces()
|
|
31
|
-
const isLoading = ref(true)
|
|
32
|
-
const isOpen = ref(false)
|
|
33
|
-
const isCreateOpen = ref(false)
|
|
34
|
-
const searchQuery = ref(
|
|
35
|
-
const switchingId = ref<string | null>(null)
|
|
21
|
+
() =>
|
|
22
|
+
isOwner.value ||
|
|
23
|
+
(config.public.dmsSaas as DmsSaasPublicRuntimeConfig | undefined)
|
|
24
|
+
?.admissionMode !== 'invitation-only',
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
const SEARCH_THRESHOLD = 6
|
|
28
|
+
const HOME_ROUTE = '/'
|
|
29
|
+
|
|
30
|
+
const { workspaces, refresh } = useMyWorkspaces()
|
|
31
|
+
const isLoading = ref(true)
|
|
32
|
+
const isOpen = ref(false)
|
|
33
|
+
const isCreateOpen = ref(false)
|
|
34
|
+
const searchQuery = ref('')
|
|
35
|
+
const switchingId = ref<string | null>(null)
|
|
36
36
|
|
|
37
37
|
const current = computed<MyWorkspace | undefined>(
|
|
38
|
-
|
|
39
|
-
)
|
|
38
|
+
() => workspaces.value.find((w) => w.isCurrent) ?? workspaces.value[0],
|
|
39
|
+
)
|
|
40
40
|
|
|
41
|
-
const showSearch = computed(() => workspaces.value.length >= SEARCH_THRESHOLD)
|
|
41
|
+
const showSearch = computed(() => workspaces.value.length >= SEARCH_THRESHOLD)
|
|
42
42
|
|
|
43
|
-
const isVisible = computed(() => !!current.value || isOwner.value)
|
|
43
|
+
const isVisible = computed(() => !!current.value || isOwner.value)
|
|
44
44
|
|
|
45
45
|
const triggerLabel = computed(
|
|
46
|
-
|
|
47
|
-
)
|
|
46
|
+
() => current.value?.name ?? t('saas.workspaces.none'),
|
|
47
|
+
)
|
|
48
48
|
|
|
49
49
|
const triggerCaption = computed(
|
|
50
|
-
|
|
51
|
-
)
|
|
50
|
+
() => current.value?.planName ?? t('saas.workspaces.current_label'),
|
|
51
|
+
)
|
|
52
52
|
|
|
53
53
|
const filtered = computed<MyWorkspace[]>(() => {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
})
|
|
54
|
+
const needle = searchQuery.value.trim().toLowerCase()
|
|
55
|
+
if (!needle) return workspaces.value
|
|
56
|
+
return workspaces.value.filter((w) => w.name.toLowerCase().includes(needle))
|
|
57
|
+
})
|
|
58
58
|
|
|
59
59
|
watch(isOpen, (open) => {
|
|
60
|
-
|
|
61
|
-
})
|
|
60
|
+
if (!open) searchQuery.value = ''
|
|
61
|
+
})
|
|
62
62
|
|
|
63
63
|
const popoverPlacement = computed(() =>
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
)
|
|
64
|
+
props.collapsed
|
|
65
|
+
? { side: 'right' as const, align: 'start' as const, sideOffset: 12 }
|
|
66
|
+
: { side: 'bottom' as const, align: 'start' as const, sideOffset: 6 },
|
|
67
|
+
)
|
|
68
68
|
|
|
69
69
|
function initialOf(name: string): string {
|
|
70
|
-
|
|
70
|
+
return (name.trim()[0] ?? '?').toUpperCase()
|
|
71
71
|
}
|
|
72
72
|
|
|
73
73
|
async function load(): Promise<void> {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
74
|
+
try {
|
|
75
|
+
await refresh()
|
|
76
|
+
} catch {
|
|
77
|
+
// Errors are swallowed on purpose: useMyWorkspaces already decides what the
|
|
78
|
+
// list becomes, and the sidebar is no place for an error card.
|
|
79
|
+
} finally {
|
|
80
|
+
isLoading.value = false
|
|
81
|
+
}
|
|
82
82
|
}
|
|
83
83
|
|
|
84
84
|
async function selectWorkspace(workspace: MyWorkspace): Promise<void> {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
85
|
+
if (workspace.isCurrent || switchingId.value) return
|
|
86
|
+
switchingId.value = workspace._id
|
|
87
|
+
try {
|
|
88
|
+
// Reloads the page on success, so the switching state never resets.
|
|
89
|
+
await useTenantSwitch(workspace._id)
|
|
90
|
+
} catch (error) {
|
|
91
|
+
switchingId.value = null
|
|
92
|
+
toast.add({
|
|
93
|
+
title: resolveApiError(error, 'saas.workspaces.switch_error'),
|
|
94
|
+
color: 'error',
|
|
95
|
+
icon: 'i-ph-warning-circle',
|
|
96
|
+
})
|
|
97
|
+
}
|
|
98
98
|
}
|
|
99
99
|
|
|
100
100
|
function openCreateModal(): void {
|
|
101
|
-
|
|
102
|
-
|
|
101
|
+
isOpen.value = false
|
|
102
|
+
isCreateOpen.value = true
|
|
103
103
|
}
|
|
104
104
|
|
|
105
105
|
async function onWorkspaceCreated(tenantId: string): Promise<void> {
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
onMounted(load)
|
|
106
|
+
isCreateOpen.value = false
|
|
107
|
+
try {
|
|
108
|
+
// Lands on the DMS homepage rather than reloading the settings screen the
|
|
109
|
+
// creation was started from — that screen belongs to the previous tenant.
|
|
110
|
+
await useTenantSwitch(tenantId, HOME_ROUTE)
|
|
111
|
+
} catch (error) {
|
|
112
|
+
toast.add({
|
|
113
|
+
title: resolveApiError(error, 'saas.workspaces.switch_error'),
|
|
114
|
+
color: 'error',
|
|
115
|
+
icon: 'i-ph-warning-circle',
|
|
116
|
+
})
|
|
117
|
+
load()
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
onMounted(load)
|
|
122
122
|
</script>
|
|
123
123
|
|
|
124
124
|
<template>
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
125
|
+
<USkeleton
|
|
126
|
+
v-if="isLoading"
|
|
127
|
+
:class="
|
|
128
|
+
collapsed ? 'mx-auto size-9 rounded-md' : 'h-[46px] w-full rounded-md'
|
|
129
|
+
"
|
|
130
|
+
/>
|
|
131
|
+
<UPopover
|
|
132
|
+
v-else-if="isVisible"
|
|
133
|
+
v-model:open="isOpen"
|
|
134
|
+
:content="popoverPlacement"
|
|
135
|
+
:ui="{ content: 'workspace-switcher-popover' }"
|
|
136
|
+
>
|
|
137
|
+
<UTooltip
|
|
138
|
+
:text="triggerLabel"
|
|
139
|
+
:disabled="!collapsed"
|
|
140
|
+
:content="{ side: 'right' }"
|
|
141
|
+
>
|
|
142
|
+
<button
|
|
143
|
+
type="button"
|
|
144
|
+
class="workspace-switcher-trigger"
|
|
145
|
+
:class="{ 'workspace-switcher-trigger--collapsed': collapsed }"
|
|
146
|
+
:aria-label="$t('saas.workspaces.switcher_title')"
|
|
147
|
+
>
|
|
148
|
+
<span class="workspace-switcher-avatar">
|
|
149
|
+
<template v-if="current">{{ initialOf(current.name) }}</template>
|
|
150
|
+
<svg v-if="!current" viewBox="0 0 24 24" aria-hidden="true">
|
|
151
|
+
<path
|
|
152
|
+
d="M3 21h18M5 21V5l7-3 7 3v16M9 9h.01M15 9h.01M9 13h.01M15 13h.01M9 17h.01M15 17h.01"
|
|
153
|
+
/>
|
|
154
|
+
</svg>
|
|
155
|
+
</span>
|
|
156
|
+
<template v-if="!collapsed">
|
|
157
|
+
<span class="min-w-0 flex-1">
|
|
158
|
+
<span class="workspace-switcher-plan">
|
|
159
|
+
{{ triggerCaption }}
|
|
160
|
+
</span>
|
|
161
|
+
<span class="workspace-switcher-name">
|
|
162
|
+
{{ triggerLabel }}
|
|
163
|
+
</span>
|
|
164
|
+
</span>
|
|
165
|
+
<span class="workspace-switcher-chevron" aria-hidden="true">
|
|
166
|
+
<svg viewBox="0 0 24 24">
|
|
167
|
+
<path d="m7 15 5 5 5-5" />
|
|
168
|
+
<path d="m7 9 5-5 5 5" />
|
|
169
|
+
</svg>
|
|
170
|
+
</span>
|
|
171
|
+
</template>
|
|
172
|
+
</button>
|
|
173
|
+
</UTooltip>
|
|
174
|
+
|
|
175
|
+
<template #content>
|
|
176
|
+
<div class="workspace-switcher-menu">
|
|
177
|
+
<p class="workspace-switcher-menu-title">
|
|
178
|
+
{{ $t('saas.workspaces.switcher_title') }}
|
|
179
|
+
</p>
|
|
180
|
+
<input
|
|
181
|
+
v-if="showSearch"
|
|
182
|
+
v-model="searchQuery"
|
|
183
|
+
class="workspace-switcher-search"
|
|
184
|
+
type="search"
|
|
185
|
+
:placeholder="$t('saas.workspaces.select')"
|
|
186
|
+
/>
|
|
187
|
+
<ul
|
|
188
|
+
v-if="filtered.length"
|
|
189
|
+
class="m-0 max-h-64 list-none overflow-y-auto p-0"
|
|
190
|
+
>
|
|
191
|
+
<li v-for="workspace in filtered" :key="workspace._id">
|
|
192
|
+
<button
|
|
193
|
+
type="button"
|
|
194
|
+
class="workspace-switcher-row"
|
|
195
|
+
:class="{
|
|
196
|
+
'workspace-switcher-row--current': workspace.isCurrent,
|
|
197
|
+
'cursor-default': workspace.isCurrent || !!switchingId,
|
|
198
|
+
}"
|
|
199
|
+
@click="selectWorkspace(workspace)"
|
|
200
|
+
>
|
|
201
|
+
<span class="workspace-switcher-row-avatar">
|
|
202
|
+
{{ initialOf(workspace.name) }}
|
|
203
|
+
</span>
|
|
204
|
+
<span
|
|
205
|
+
class="workspace-switcher-row-name"
|
|
206
|
+
:class="{
|
|
207
|
+
'workspace-switcher-row-name--current': workspace.isCurrent,
|
|
208
|
+
}"
|
|
209
|
+
>
|
|
210
|
+
{{ workspace.name }}
|
|
211
|
+
</span>
|
|
212
|
+
<span
|
|
213
|
+
v-if="workspace.planName"
|
|
214
|
+
class="workspace-switcher-row-plan"
|
|
215
|
+
>
|
|
216
|
+
{{ workspace.planName }}
|
|
217
|
+
</span>
|
|
218
|
+
<span
|
|
219
|
+
v-if="switchingId === workspace._id"
|
|
220
|
+
class="workspace-switcher-spinner"
|
|
221
|
+
aria-hidden="true"
|
|
222
|
+
/>
|
|
223
|
+
<span
|
|
224
|
+
v-else-if="workspace.isCurrent"
|
|
225
|
+
class="workspace-switcher-check"
|
|
226
|
+
aria-hidden="true"
|
|
227
|
+
>
|
|
228
|
+
<svg viewBox="0 0 24 24"><path d="m4 12 5 5L20 6" /></svg>
|
|
229
|
+
</span>
|
|
230
|
+
</button>
|
|
231
|
+
</li>
|
|
232
|
+
</ul>
|
|
233
|
+
<p v-else-if="workspaces.length" class="workspace-switcher-empty">
|
|
234
|
+
{{ $t('saas.workspaces.no_results') }}
|
|
235
|
+
</p>
|
|
236
|
+
<div v-if="workspaces.length" class="workspace-switcher-separator" />
|
|
237
|
+
<button
|
|
238
|
+
v-if="canCreateWorkspace"
|
|
239
|
+
type="button"
|
|
240
|
+
class="workspace-switcher-create"
|
|
241
|
+
@click="openCreateModal"
|
|
242
|
+
>
|
|
243
|
+
+ {{ $t('saas.workspaces.create.button') }}
|
|
244
|
+
</button>
|
|
245
|
+
</div>
|
|
246
|
+
</template>
|
|
247
|
+
</UPopover>
|
|
248
|
+
|
|
249
|
+
<UModal
|
|
250
|
+
v-model:open="isCreateOpen"
|
|
251
|
+
:title="$t('saas.workspaces.create.title')"
|
|
252
|
+
:description="$t('saas.workspaces.create.self_serve.description')"
|
|
253
|
+
>
|
|
254
|
+
<template #body>
|
|
255
|
+
<DmsSaasWorkspaceCreateModal :on-success-callback="onWorkspaceCreated" />
|
|
256
|
+
</template>
|
|
257
|
+
</UModal>
|
|
247
258
|
</template>
|
|
259
|
+
|
|
260
|
+
<style scoped>
|
|
261
|
+
:global(.workspace-switcher-popover) {
|
|
262
|
+
padding: 0;
|
|
263
|
+
border: 0;
|
|
264
|
+
border-radius: 0;
|
|
265
|
+
background: transparent;
|
|
266
|
+
box-shadow: none;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
.workspace-switcher-trigger {
|
|
270
|
+
display: flex;
|
|
271
|
+
align-items: center;
|
|
272
|
+
gap: 10px;
|
|
273
|
+
width: 100%;
|
|
274
|
+
padding: 9px 10px;
|
|
275
|
+
border: 1px solid rgba(120, 140, 200, 0.1);
|
|
276
|
+
border-radius: 6px;
|
|
277
|
+
background: #15151b;
|
|
278
|
+
color: #f1f1f3;
|
|
279
|
+
text-align: left;
|
|
280
|
+
transition: border-color 150ms ease;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
.workspace-switcher-trigger:hover {
|
|
284
|
+
border-color: rgba(140, 160, 220, 0.18);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
.workspace-switcher-trigger--collapsed {
|
|
288
|
+
width: 36px;
|
|
289
|
+
height: 36px;
|
|
290
|
+
justify-content: center;
|
|
291
|
+
padding: 4px;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
.workspace-switcher-avatar,
|
|
295
|
+
.workspace-switcher-row-avatar {
|
|
296
|
+
display: inline-flex;
|
|
297
|
+
flex-shrink: 0;
|
|
298
|
+
align-items: center;
|
|
299
|
+
justify-content: center;
|
|
300
|
+
color: #fff;
|
|
301
|
+
font-weight: 700;
|
|
302
|
+
background: linear-gradient(135deg, #8b5cf6, #7c3aed);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
.workspace-switcher-avatar {
|
|
306
|
+
width: 26px;
|
|
307
|
+
height: 26px;
|
|
308
|
+
border-radius: 6px;
|
|
309
|
+
font-size: 13px;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
.workspace-switcher-avatar svg,
|
|
313
|
+
.workspace-switcher-chevron svg,
|
|
314
|
+
.workspace-switcher-check svg {
|
|
315
|
+
width: 100%;
|
|
316
|
+
height: 100%;
|
|
317
|
+
fill: none;
|
|
318
|
+
stroke: currentColor;
|
|
319
|
+
stroke-linecap: round;
|
|
320
|
+
stroke-linejoin: round;
|
|
321
|
+
stroke-width: 1.8;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
.workspace-switcher-avatar svg {
|
|
325
|
+
width: 16px;
|
|
326
|
+
height: 16px;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
.workspace-switcher-trigger:not(.workspace-switcher-trigger--collapsed)
|
|
330
|
+
> span:nth-child(2) {
|
|
331
|
+
display: flex;
|
|
332
|
+
min-width: 0;
|
|
333
|
+
flex-direction: column;
|
|
334
|
+
line-height: 1.25;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
.workspace-switcher-plan,
|
|
338
|
+
.workspace-switcher-row-plan {
|
|
339
|
+
color: #a78bfa;
|
|
340
|
+
font-family: 'JetBrains Mono', ui-monospace, monospace;
|
|
341
|
+
letter-spacing: 0.08em;
|
|
342
|
+
text-transform: uppercase;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
.workspace-switcher-plan {
|
|
346
|
+
font-size: 10px;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
.workspace-switcher-name {
|
|
350
|
+
overflow: hidden;
|
|
351
|
+
color: #f1f1f3;
|
|
352
|
+
font-family: 'Space Grotesk', Inter, ui-sans-serif, sans-serif;
|
|
353
|
+
font-size: 13.5px;
|
|
354
|
+
font-weight: 600;
|
|
355
|
+
letter-spacing: -0.01em;
|
|
356
|
+
text-overflow: ellipsis;
|
|
357
|
+
white-space: nowrap;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
.workspace-switcher-chevron {
|
|
361
|
+
display: inline-flex;
|
|
362
|
+
width: 16px;
|
|
363
|
+
height: 16px;
|
|
364
|
+
margin-left: auto;
|
|
365
|
+
flex-shrink: 0;
|
|
366
|
+
color: #71717a;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
.workspace-switcher-menu {
|
|
370
|
+
display: flex;
|
|
371
|
+
width: 224px;
|
|
372
|
+
flex-direction: column;
|
|
373
|
+
gap: 2px;
|
|
374
|
+
padding: 5px;
|
|
375
|
+
border: 1px solid rgba(140, 160, 220, 0.18);
|
|
376
|
+
border-radius: 8px;
|
|
377
|
+
background: #15151b;
|
|
378
|
+
box-shadow: 0 16px 48px -16px rgb(0 0 0 / 70%);
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
.workspace-switcher-menu-title,
|
|
382
|
+
.workspace-switcher-empty {
|
|
383
|
+
color: #71717a;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
.workspace-switcher-menu-title {
|
|
387
|
+
margin: 0;
|
|
388
|
+
padding: 5px 9px 3px;
|
|
389
|
+
font-size: 10.5px;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
.workspace-switcher-search {
|
|
393
|
+
width: 100%;
|
|
394
|
+
padding: 6px 8px;
|
|
395
|
+
border: 1px solid rgba(140, 160, 220, 0.18);
|
|
396
|
+
border-radius: 6px;
|
|
397
|
+
background: #0a0b12;
|
|
398
|
+
color: #f1f1f3;
|
|
399
|
+
font-size: 12px;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
.workspace-switcher-row {
|
|
403
|
+
display: flex;
|
|
404
|
+
width: 100%;
|
|
405
|
+
align-items: center;
|
|
406
|
+
gap: 9px;
|
|
407
|
+
padding: 8px 9px;
|
|
408
|
+
border: 0;
|
|
409
|
+
border-radius: 6px;
|
|
410
|
+
background: transparent;
|
|
411
|
+
color: #9b9ba4;
|
|
412
|
+
font-size: 13px;
|
|
413
|
+
text-align: left;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
.workspace-switcher-row:hover {
|
|
417
|
+
background: #1f1f24;
|
|
418
|
+
color: #f1f1f3;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
.workspace-switcher-row--current {
|
|
422
|
+
background: color-mix(in oklab, #8b5cf6 12%, transparent);
|
|
423
|
+
color: #f1f1f3;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
.workspace-switcher-row-avatar {
|
|
427
|
+
width: 22px;
|
|
428
|
+
height: 22px;
|
|
429
|
+
border-radius: 5px;
|
|
430
|
+
font-size: 11px;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
.workspace-switcher-row-name {
|
|
434
|
+
min-width: 0;
|
|
435
|
+
flex: 1;
|
|
436
|
+
overflow: hidden;
|
|
437
|
+
text-overflow: ellipsis;
|
|
438
|
+
white-space: nowrap;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
.workspace-switcher-row-name--current {
|
|
442
|
+
color: #f1f1f3;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
.workspace-switcher-row-plan {
|
|
446
|
+
flex-shrink: 0;
|
|
447
|
+
font-size: 9.5px;
|
|
448
|
+
white-space: nowrap;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
.workspace-switcher-check {
|
|
452
|
+
display: inline-flex;
|
|
453
|
+
width: 16px;
|
|
454
|
+
height: 16px;
|
|
455
|
+
flex-shrink: 0;
|
|
456
|
+
color: #a78bfa;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
.workspace-switcher-spinner {
|
|
460
|
+
width: 14px;
|
|
461
|
+
height: 14px;
|
|
462
|
+
flex-shrink: 0;
|
|
463
|
+
border: 2px solid #71717a;
|
|
464
|
+
border-top-color: #a78bfa;
|
|
465
|
+
border-radius: 50%;
|
|
466
|
+
animation: workspace-switcher-spin 700ms linear infinite;
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
.workspace-switcher-separator {
|
|
470
|
+
height: 1px;
|
|
471
|
+
margin: 4px 2px;
|
|
472
|
+
background: rgba(120, 140, 200, 0.06);
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
.workspace-switcher-create {
|
|
476
|
+
justify-content: flex-start;
|
|
477
|
+
width: 100%;
|
|
478
|
+
padding: 8px 9px;
|
|
479
|
+
border: 0;
|
|
480
|
+
background: transparent;
|
|
481
|
+
color: #a78bfa;
|
|
482
|
+
font-size: 13px;
|
|
483
|
+
text-align: left;
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
.workspace-switcher-empty {
|
|
487
|
+
margin: 0;
|
|
488
|
+
padding: 8px 9px;
|
|
489
|
+
font-size: 13px;
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
@keyframes workspace-switcher-spin {
|
|
493
|
+
to {
|
|
494
|
+
transform: rotate(360deg);
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
</style>
|
|
@@ -448,7 +448,7 @@ onMounted(async () => {
|
|
|
448
448
|
<i18n-t keypath="saas.register.legal_acceptance" tag="span">
|
|
449
449
|
<template #terms_and_conditions>
|
|
450
450
|
<DmsLink
|
|
451
|
-
to="/
|
|
451
|
+
to="/terms-and-conditions"
|
|
452
452
|
target="_blank"
|
|
453
453
|
class="text-primary underline"
|
|
454
454
|
>
|
|
@@ -457,7 +457,7 @@ onMounted(async () => {
|
|
|
457
457
|
</template>
|
|
458
458
|
<template #privacy_policy>
|
|
459
459
|
<DmsLink
|
|
460
|
-
to="/
|
|
460
|
+
to="/privacy-policy"
|
|
461
461
|
target="_blank"
|
|
462
462
|
class="text-primary underline"
|
|
463
463
|
>
|
|
@@ -6,9 +6,9 @@ const TERMS_OF_USE_LABEL = "saas.legal.terms_of_use";
|
|
|
6
6
|
const TERMS_AND_CONDITIONS_LABEL = "saas.legal.terms_and_conditions";
|
|
7
7
|
const PRIVACY_POLICY_LABEL = "saas.legal.privacy_policy";
|
|
8
8
|
|
|
9
|
-
const TERMS_OF_USE_ROUTE = "/
|
|
10
|
-
const TERMS_AND_CONDITIONS_ROUTE = "/
|
|
11
|
-
const PRIVACY_POLICY_ROUTE = "/
|
|
9
|
+
const TERMS_OF_USE_ROUTE = "/terms-of-use";
|
|
10
|
+
const TERMS_AND_CONDITIONS_ROUTE = "/terms-and-conditions";
|
|
11
|
+
const PRIVACY_POLICY_ROUTE = "/privacy-policy";
|
|
12
12
|
|
|
13
13
|
const ORDER_TERMS_OF_USE = 100;
|
|
14
14
|
const ORDER_TERMS_AND_CONDITIONS = 200;
|
|
@@ -2,7 +2,7 @@ import { defineAsyncComponent, type Component } from 'vue'
|
|
|
2
2
|
import type {
|
|
3
3
|
DmsFrontendModule,
|
|
4
4
|
DmsFrontendSdk,
|
|
5
|
-
} from '#dms
|
|
5
|
+
} from '#dms/frontend-module'
|
|
6
6
|
import authLinks from './app/plugins/auth-links'
|
|
7
7
|
import billingDataTypes from './app/plugins/billing-data-types'
|
|
8
8
|
import footerLinks from './app/plugins/footer-links'
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@antelopejs/dms-saas",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.7",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -42,9 +42,9 @@
|
|
|
42
42
|
"zod": "^3.24.4"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
|
-
"@antelopejs/core": "
|
|
46
|
-
"@antelopejs/dms": ">=0.3.
|
|
47
|
-
"@antelopejs/dms-frontend": "
|
|
45
|
+
"@antelopejs/core": "1.8.0",
|
|
46
|
+
"@antelopejs/dms": ">=0.3.5 <1.0.0",
|
|
47
|
+
"@antelopejs/dms-frontend": "0.2.0",
|
|
48
48
|
"@antelopejs/file-storage-local": "0.1.4",
|
|
49
49
|
"@antelopejs/mongodb": "1.3.0",
|
|
50
50
|
"@antelopejs/tooling-configs": ">=0.0.6 <1.0.0",
|
|
@@ -72,7 +72,7 @@
|
|
|
72
72
|
},
|
|
73
73
|
"scripts": {
|
|
74
74
|
"build": "pnpm --filter @antelopejs/interface-dms-saas build && rimraf dist && tsc -p tsconfig.build.json",
|
|
75
|
-
"test:frontend": "ajs
|
|
75
|
+
"test:frontend": "ajs dms verify-source --layer node_modules/@antelopejs/dms/frontend-vue --module frontend-vue",
|
|
76
76
|
"frontend:dev": "cd playground && pnpm frontend:dev",
|
|
77
77
|
"dev": "ajs project run -w -p playground",
|
|
78
78
|
"stripe:webhooks": "stripe listen --forward-to localhost:5010/api/saas/webhooks/stripe",
|