@goweekdays/layer-common 1.4.5 → 1.5.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/CHANGELOG.md +12 -0
- package/components/Input/Currency.vue +60 -0
- package/components/Input/Infinity.vue +112 -0
- package/components/Input/Slug.vue +50 -0
- package/components/InvitationMain.vue +4 -4
- package/components/MemberInvite.vue +4 -1
- package/composables/useOrg.ts +1 -0
- package/composables/usePromo.ts +78 -0
- package/composables/useSubscription.ts +13 -0
- package/nuxt.config.ts +3 -2
- package/package.json +1 -1
- package/pages/index.vue +2 -1
- package/pages/member-suspended.vue +1 -1
- package/types/org.d.ts +1 -0
- package/types/promo.d.ts +22 -0
package/CHANGELOG.md
CHANGED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<v-text-field
|
|
3
|
+
type="text"
|
|
4
|
+
inputmode="numeric"
|
|
5
|
+
:model-value="display"
|
|
6
|
+
@update:model-value="onInput"
|
|
7
|
+
@blur="format"
|
|
8
|
+
@keydown="onKeydown"
|
|
9
|
+
v-bind="$attrs"
|
|
10
|
+
>
|
|
11
|
+
<!-- forward all slots -->
|
|
12
|
+
<template v-for="(_, name) in $slots" #[name]="slotProps">
|
|
13
|
+
<slot :name="name" v-bind="slotProps" />
|
|
14
|
+
</template>
|
|
15
|
+
</v-text-field>
|
|
16
|
+
</template>
|
|
17
|
+
|
|
18
|
+
<script setup lang="ts">
|
|
19
|
+
/**
|
|
20
|
+
* numeric v-model
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
const localProps = defineProps({
|
|
24
|
+
step: {
|
|
25
|
+
type: Number,
|
|
26
|
+
default: 1,
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
const model = defineModel<number>({ default: 0 });
|
|
30
|
+
|
|
31
|
+
const display = ref<string>("");
|
|
32
|
+
|
|
33
|
+
const parse = (v: string) => Number(v.replace(/[^0-9]/g, "") || 0);
|
|
34
|
+
|
|
35
|
+
const format = () => {
|
|
36
|
+
display.value = model.value.toLocaleString("en-US");
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
function onInput(val: string) {
|
|
40
|
+
if (!/^[0-9,]*$/.test(val)) return;
|
|
41
|
+
model.value = parse(val);
|
|
42
|
+
display.value = val;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function onKeydown(e: KeyboardEvent) {
|
|
46
|
+
if (e.key === "ArrowUp") {
|
|
47
|
+
e.preventDefault();
|
|
48
|
+
model.value += localProps.step;
|
|
49
|
+
format();
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (e.key === "ArrowDown") {
|
|
53
|
+
e.preventDefault();
|
|
54
|
+
model.value = Math.max(0, model.value - localProps.step);
|
|
55
|
+
format();
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
watch(() => model.value, format, { immediate: true });
|
|
60
|
+
</script>
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<v-text-field
|
|
3
|
+
type="text"
|
|
4
|
+
inputmode="numeric"
|
|
5
|
+
:model-value="display"
|
|
6
|
+
@update:model-value="onInput"
|
|
7
|
+
@update:focused="doAutoCorrect"
|
|
8
|
+
@keydown="onKeydown"
|
|
9
|
+
v-bind="$attrs"
|
|
10
|
+
>
|
|
11
|
+
<!-- forward all slots -->
|
|
12
|
+
<template v-for="(_, name) in $slots" #[name]="slotProps">
|
|
13
|
+
<slot :name="name" v-bind="slotProps" />
|
|
14
|
+
</template>
|
|
15
|
+
</v-text-field>
|
|
16
|
+
</template>
|
|
17
|
+
|
|
18
|
+
<script setup lang="ts">
|
|
19
|
+
/**
|
|
20
|
+
* Numeric input that displays ∞ when value is 0
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
const localProps = defineProps({
|
|
24
|
+
step: {
|
|
25
|
+
type: Number,
|
|
26
|
+
default: 1,
|
|
27
|
+
},
|
|
28
|
+
minValue: {
|
|
29
|
+
type: Number,
|
|
30
|
+
default: 0,
|
|
31
|
+
},
|
|
32
|
+
autoCorrect: {
|
|
33
|
+
type: Boolean,
|
|
34
|
+
default: false,
|
|
35
|
+
},
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
const model = defineModel<number>({ default: 0 });
|
|
39
|
+
|
|
40
|
+
const display = ref<string>("");
|
|
41
|
+
|
|
42
|
+
const INFINITY_SYMBOL = "∞";
|
|
43
|
+
|
|
44
|
+
const parse = (v: string) => {
|
|
45
|
+
if (v === INFINITY_SYMBOL || v === "") return 0;
|
|
46
|
+
return Number(v.replace(/[^0-9]/g, "") || 0);
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const format = () => {
|
|
50
|
+
display.value =
|
|
51
|
+
model.value === 0 ? INFINITY_SYMBOL : model.value.toLocaleString("en-US");
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
function doAutoCorrect() {
|
|
55
|
+
if (!localProps.autoCorrect) return;
|
|
56
|
+
|
|
57
|
+
// Auto-correct if value is invalid (> 0 but <= minValue)
|
|
58
|
+
if (model.value > 0 && model.value <= localProps.minValue) {
|
|
59
|
+
model.value = localProps.minValue + 1;
|
|
60
|
+
format();
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function onInput(val: string) {
|
|
65
|
+
// Allow infinity symbol, empty, or numeric input
|
|
66
|
+
if (val === INFINITY_SYMBOL || val === "") {
|
|
67
|
+
model.value = 0;
|
|
68
|
+
display.value = val;
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// If current display is infinity and user types a number, replace entirely
|
|
73
|
+
if (display.value === INFINITY_SYMBOL) {
|
|
74
|
+
const numericOnly = val.replace(/[^0-9]/g, "");
|
|
75
|
+
if (numericOnly) {
|
|
76
|
+
model.value = Number(numericOnly);
|
|
77
|
+
display.value = numericOnly;
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (!/^[0-9,]*$/.test(val)) return;
|
|
83
|
+
model.value = parse(val);
|
|
84
|
+
display.value = val;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function onKeydown(e: KeyboardEvent) {
|
|
88
|
+
if (e.key === "Enter") {
|
|
89
|
+
doAutoCorrect();
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (e.key === "ArrowUp") {
|
|
93
|
+
e.preventDefault();
|
|
94
|
+
// If value is 0 (infinity), start from minValue + 1
|
|
95
|
+
if (model.value === 0) {
|
|
96
|
+
model.value = localProps.minValue + 1;
|
|
97
|
+
} else {
|
|
98
|
+
model.value += localProps.step;
|
|
99
|
+
}
|
|
100
|
+
format();
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (e.key === "ArrowDown") {
|
|
104
|
+
e.preventDefault();
|
|
105
|
+
// Don't go below 0
|
|
106
|
+
model.value = Math.max(0, model.value - localProps.step);
|
|
107
|
+
format();
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
watch(() => model.value, format, { immediate: true });
|
|
112
|
+
</script>
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<v-text-field
|
|
3
|
+
type="text"
|
|
4
|
+
:model-value="display"
|
|
5
|
+
@update:model-value="onInput"
|
|
6
|
+
@blur="format"
|
|
7
|
+
@keydown="onKeydown"
|
|
8
|
+
v-bind="$attrs"
|
|
9
|
+
>
|
|
10
|
+
<!-- forward all slots -->
|
|
11
|
+
<template v-for="(_, name) in $slots" #[name]="slotProps">
|
|
12
|
+
<slot :name="name" v-bind="slotProps" />
|
|
13
|
+
</template>
|
|
14
|
+
</v-text-field>
|
|
15
|
+
</template>
|
|
16
|
+
|
|
17
|
+
<script setup lang="ts">
|
|
18
|
+
/**
|
|
19
|
+
* Input that converts spaces to hyphens and forces lowercase, useful for slugs, codes, or identifiers
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
const model = defineModel<string>({ default: "" });
|
|
23
|
+
|
|
24
|
+
const display = ref<string>("");
|
|
25
|
+
|
|
26
|
+
const transform = (v: string) => {
|
|
27
|
+
return v.replace(/\s+/g, "-").toLowerCase();
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const format = () => {
|
|
31
|
+
display.value = transform(model.value);
|
|
32
|
+
model.value = display.value;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
function onInput(val: string) {
|
|
36
|
+
const transformed = transform(val);
|
|
37
|
+
model.value = transformed;
|
|
38
|
+
display.value = transformed;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function onKeydown(e: KeyboardEvent) {
|
|
42
|
+
if (e.key === " ") {
|
|
43
|
+
e.preventDefault();
|
|
44
|
+
model.value = transform(display.value + "-");
|
|
45
|
+
display.value = model.value;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
watch(() => model.value, format, { immediate: true });
|
|
50
|
+
</script>
|
|
@@ -66,7 +66,6 @@
|
|
|
66
66
|
<v-dialog v-model="dialogView" width="450" persistent>
|
|
67
67
|
<MemberInvite
|
|
68
68
|
title="Invite Details"
|
|
69
|
-
:app="props.app"
|
|
70
69
|
v-model="invitation"
|
|
71
70
|
@close="setInvite({ dialog: false })"
|
|
72
71
|
@cancel:invite="handleOpenCancelInvite()"
|
|
@@ -141,6 +140,8 @@ const pageRange = ref("-- - -- of --");
|
|
|
141
140
|
const items = ref<Array<Record<string, any>>>([]);
|
|
142
141
|
const { headerSearch } = useLocal();
|
|
143
142
|
|
|
143
|
+
const isOrg = computed(() => props.app === "org");
|
|
144
|
+
|
|
144
145
|
const {
|
|
145
146
|
data: getInviteReq,
|
|
146
147
|
refresh: getInvitations,
|
|
@@ -154,6 +155,7 @@ const {
|
|
|
154
155
|
status: props.status,
|
|
155
156
|
type: "user-invite,member-invite",
|
|
156
157
|
org: props.org,
|
|
158
|
+
app: isOrg.value ? "" : props.app,
|
|
157
159
|
})
|
|
158
160
|
);
|
|
159
161
|
|
|
@@ -179,9 +181,7 @@ function setInvite({
|
|
|
179
181
|
} = {}) {
|
|
180
182
|
Object.assign(invitation.value, JSON.parse(JSON.stringify(data)));
|
|
181
183
|
|
|
182
|
-
|
|
183
|
-
dialogView.value = dialog;
|
|
184
|
-
}
|
|
184
|
+
dialogView.value = dialog;
|
|
185
185
|
}
|
|
186
186
|
|
|
187
187
|
function tableRowClickHandler(_: any, data: any) {
|
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
item-value="code"
|
|
27
27
|
:rules="isMutable ? [requiredRule] : []"
|
|
28
28
|
:loading="loadingApps"
|
|
29
|
+
:readonly="!isMutable"
|
|
29
30
|
></v-select>
|
|
30
31
|
</v-col>
|
|
31
32
|
</v-row>
|
|
@@ -189,7 +190,9 @@ const invite = defineModel<TMemberInvitation>({
|
|
|
189
190
|
default: () => useMember().invitation.value,
|
|
190
191
|
});
|
|
191
192
|
|
|
192
|
-
|
|
193
|
+
if (localProps.mode === "add") {
|
|
194
|
+
invite.value.app = localProps.app;
|
|
195
|
+
}
|
|
193
196
|
|
|
194
197
|
const message = defineModel("message", { default: "" });
|
|
195
198
|
|
package/composables/useOrg.ts
CHANGED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
export default function usePromo() {
|
|
2
|
+
const promo = ref<TPromo>({
|
|
3
|
+
_id: "",
|
|
4
|
+
code: "",
|
|
5
|
+
type: "fixed",
|
|
6
|
+
flatRate: 0,
|
|
7
|
+
fixedRate: 0,
|
|
8
|
+
tiers: [],
|
|
9
|
+
currency: "",
|
|
10
|
+
startDate: "",
|
|
11
|
+
endDate: "",
|
|
12
|
+
seats: 0,
|
|
13
|
+
usage: 0,
|
|
14
|
+
apps: [],
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
function add(value: TPromo) {
|
|
18
|
+
return $fetch<Record<string, any>>("/api/promos", {
|
|
19
|
+
method: "POST",
|
|
20
|
+
body: value,
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async function getAll({
|
|
25
|
+
page = 1,
|
|
26
|
+
limit = 10,
|
|
27
|
+
search = "",
|
|
28
|
+
status = "",
|
|
29
|
+
} = {}) {
|
|
30
|
+
return $fetch<Record<string, any>>("/api/promos", {
|
|
31
|
+
method: "GET",
|
|
32
|
+
query: { page, limit, search, status },
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async function getByCode(code: string) {
|
|
37
|
+
return $fetch<Record<string, any>>(`/api/promos/code/${code}`, {
|
|
38
|
+
method: "GET",
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async function getById(id: string) {
|
|
43
|
+
return $fetch<Record<string, any>>(`/api/promos/id/${id}`, {
|
|
44
|
+
method: "GET",
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async function updateById(
|
|
49
|
+
id: string,
|
|
50
|
+
value: Pick<
|
|
51
|
+
TPromo,
|
|
52
|
+
| "code"
|
|
53
|
+
| "apps"
|
|
54
|
+
| "type"
|
|
55
|
+
| "flatRate"
|
|
56
|
+
| "fixedRate"
|
|
57
|
+
| "tiers"
|
|
58
|
+
| "currency"
|
|
59
|
+
| "startDate"
|
|
60
|
+
| "endDate"
|
|
61
|
+
| "seats"
|
|
62
|
+
| "usage"
|
|
63
|
+
>
|
|
64
|
+
) {
|
|
65
|
+
return $fetch<Record<string, any>>(`/api/promos/id/${id}`, {
|
|
66
|
+
method: "PATCH",
|
|
67
|
+
body: value,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async function deleteById(id: string) {
|
|
72
|
+
return $fetch<Record<string, any>>(`/api/promos/${id}`, {
|
|
73
|
+
method: "DELETE",
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return { promo, add, getAll, getByCode, getById, updateById, deleteById };
|
|
78
|
+
}
|
|
@@ -42,8 +42,21 @@ export default function useSubscription() {
|
|
|
42
42
|
});
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
+
function getAll({ page = 1, limit = 20, search = "", status = "" } = {}) {
|
|
46
|
+
return $fetch<Record<string, any>>(`/api/subscriptions`, {
|
|
47
|
+
method: "GET",
|
|
48
|
+
query: {
|
|
49
|
+
page,
|
|
50
|
+
limit,
|
|
51
|
+
search,
|
|
52
|
+
status,
|
|
53
|
+
},
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
|
|
45
57
|
return {
|
|
46
58
|
subscription,
|
|
59
|
+
getAll,
|
|
47
60
|
getByOrg,
|
|
48
61
|
getTransactionsById,
|
|
49
62
|
updateSeatById,
|
package/nuxt.config.ts
CHANGED
|
@@ -69,8 +69,8 @@ export default defineNuxtConfig({
|
|
|
69
69
|
"/api/entities/**": { proxy: `${process.env.API_CORE}/api/entities/**` },
|
|
70
70
|
"/api/workflows/**": { proxy: `${process.env.API_CORE}/api/workflows/**` },
|
|
71
71
|
"/api/roles/**": { proxy: `${process.env.API_CORE}/api/roles/**` },
|
|
72
|
-
"/api/
|
|
73
|
-
proxy: `${process.env.API_CORE}/api/
|
|
72
|
+
"/api/promos/**": {
|
|
73
|
+
proxy: `${process.env.API_CORE}/api/promos/**`,
|
|
74
74
|
},
|
|
75
75
|
"/api/verifications/**": {
|
|
76
76
|
proxy: `${process.env.API_CORE}/api/verifications/**`,
|
|
@@ -81,6 +81,7 @@ export default defineNuxtConfig({
|
|
|
81
81
|
"/api/public/**": {
|
|
82
82
|
proxy: `${process.env.S3_BUCKET_ENDPOINT}/**`,
|
|
83
83
|
},
|
|
84
|
+
"/api/job/posts/**": { proxy: `${process.env.API_CORE}/api/job/posts/**` },
|
|
84
85
|
},
|
|
85
86
|
|
|
86
87
|
vite: {
|
package/package.json
CHANGED
package/pages/index.vue
CHANGED
|
@@ -94,13 +94,14 @@ const isPartnerApp = computed(() =>
|
|
|
94
94
|
].includes(APP)
|
|
95
95
|
);
|
|
96
96
|
|
|
97
|
-
const { data: member } = await useLazyAsyncData(
|
|
97
|
+
const { data: member, refresh: refreshMember } = await useLazyAsyncData(
|
|
98
98
|
"get-member-by-app",
|
|
99
99
|
() => getByApp({ app: APP, user: loggedInUser() }),
|
|
100
100
|
{ immediate: isPartnerApp.value }
|
|
101
101
|
);
|
|
102
102
|
|
|
103
103
|
function handleSignin() {
|
|
104
|
+
refreshMember();
|
|
104
105
|
if (APP === "main") {
|
|
105
106
|
navigateTo({ name: "feeds" });
|
|
106
107
|
} else if (APP === "admin") {
|
package/types/org.d.ts
CHANGED
package/types/promo.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
declare type TPromo = {
|
|
2
|
+
_id?: string;
|
|
3
|
+
code: string;
|
|
4
|
+
type: "flat" | "fixed" | "volume";
|
|
5
|
+
flatRate?: number; // regardless of seats
|
|
6
|
+
fixedRate?: number; // per seat
|
|
7
|
+
tiers?: Array<{
|
|
8
|
+
minSeats: number;
|
|
9
|
+
maxSeats: number;
|
|
10
|
+
rate: number;
|
|
11
|
+
}>;
|
|
12
|
+
currency: string;
|
|
13
|
+
startDate?: Date | string;
|
|
14
|
+
endDate?: Date | string;
|
|
15
|
+
seats?: number;
|
|
16
|
+
usage?: number;
|
|
17
|
+
apps?: string[];
|
|
18
|
+
status?: "active" | "inactive" | "expired";
|
|
19
|
+
createdAt?: Date | string;
|
|
20
|
+
updatedAt?: Date | string;
|
|
21
|
+
deletedAt?: Date | string;
|
|
22
|
+
};
|