@hostlink/nuxt-light 0.0.118 → 1.0.2

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/dist/module.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "light",
3
3
  "configKey": "light",
4
- "version": "0.0.118"
4
+ "version": "1.0.2"
5
5
  }
package/dist/module.mjs CHANGED
@@ -1,5 +1,4 @@
1
1
  import { defineNuxtModule, createResolver, addComponentsDir, addImports, addPlugin } from '@nuxt/kit';
2
- import AutoImport from 'unplugin-auto-import/vite';
3
2
 
4
3
  const module = defineNuxtModule({
5
4
  meta: {
@@ -20,17 +19,6 @@ const module = defineNuxtModule({
20
19
  rel: "stylesheet",
21
20
  href: "https://fonts.googleapis.com/css2?family=Noto+Sans&family=Noto+Sans+HK&family=Noto+Sans+TC&family=Material+Symbols+Outlined"
22
21
  });
23
- nuxt.options.vite.plugins?.push(AutoImport({
24
- imports: [{
25
- "quasar": [
26
- "useQuasar"
27
- ]
28
- }, {
29
- "vue-i18n": [
30
- "useI18n"
31
- ]
32
- }]
33
- }));
34
22
  addComponentsDir({
35
23
  path: resolver.resolve("./runtime/components")
36
24
  });
@@ -60,7 +48,8 @@ const module = defineNuxtModule({
60
48
  { name: "getModelFields", from },
61
49
  { name: "getModelField", from },
62
50
  { name: "getGQLFields", from },
63
- { name: "model", from }
51
+ { name: "model", from },
52
+ { name: "notify", from }
64
53
  ]);
65
54
  addPlugin({
66
55
  src: resolver.resolve("./runtime/plugin"),
@@ -16,6 +16,7 @@ const appVersion = config.public.appVersion ?? '0.0.1';
16
16
 
17
17
  const quasar = useQuasar();
18
18
  const tt = await q({
19
+ system: ["devMode"],
19
20
  app: ["menus", "viewAsMode", "languages", { i18nMessages: ["name", "value"] }],
20
21
  my: ['username', 'first_name', 'last_name', 'roles', "styles", "language", f('granted_storage:granted', { right: "system.storage" }, [])],
21
22
  })
@@ -194,6 +195,9 @@ const containerStyle = computed(() => {
194
195
 
195
196
  <q-toolbar-title>
196
197
  {{ light.getCompany() }}
198
+ <template v-if="tt.system.devMode">
199
+ - Development mode
200
+ </template>
197
201
  </q-toolbar-title>
198
202
 
199
203
  <q-space />
@@ -212,7 +216,7 @@ const containerStyle = computed(() => {
212
216
  </q-btn>
213
217
 
214
218
  <q-btn icon="sym_o_storage" flat round dense class="q-mr-sm" v-if="my.granted_storage"
215
- :color="storageColor" >
219
+ :color="storageColor">
216
220
  <q-menu>
217
221
  <q-card style="width:250px">
218
222
  <q-card-section>
@@ -304,7 +308,7 @@ const containerStyle = computed(() => {
304
308
  </q-drawer>
305
309
 
306
310
  <q-page-container :class="containerClass" :style="containerStyle">
307
-
311
+
308
312
  <!-- Error message -->
309
313
  <q-banner dense inline-actions class="bg-grey-4 q-ma-md" v-for="error in errors" rounded>
310
314
  {{ error }}
@@ -0,0 +1,78 @@
1
+
2
+ <script setup>
3
+ import * as XLSX from "xlsx";
4
+ import { computed, ref, useAttrs, useSlots } from "vue";
5
+ import { useLight } from "#imports"
6
+
7
+ const emit = defineEmits(["update:modelValue"]);
8
+
9
+ const light = useLight();
10
+ const attrs = {
11
+ ...{
12
+ filled: light.getStyle("inputFilled"),
13
+ outlined: light.getStyle("inputOutlined"),
14
+ standout: light.getStyle("inputStandout"),
15
+ rounded: light.getStyle("inputRounded"),
16
+ dense: light.getStyle("inputDense"),
17
+ square: light.getStyle("inputSquare"),
18
+ stackLabel: light.getStyle("inputStackLabel")
19
+ },
20
+ ...useAttrs(),
21
+
22
+ }
23
+ const loading = ref(false);
24
+ const localData = ref([]);
25
+ const hasFile = ref(false);
26
+ const file = ref(null);
27
+ const onChange = () => {
28
+ hasFile.value = true;
29
+ loading.value = true;
30
+ file.value.files[0].arrayBuffer().then((data) => {
31
+ const workbook = XLSX.read(data);
32
+ const sheet = workbook.Sheets[workbook.SheetNames[0]];
33
+ const json = XLSX.utils.sheet_to_json(sheet);
34
+ localData.value = json;
35
+ loading.value = false;
36
+
37
+ emit("update:modelValue", json);
38
+
39
+ });
40
+
41
+ }
42
+
43
+ const onClear = () => {
44
+ hasFile.value = false;
45
+ localData.value = [];
46
+ emit("update:modelValue", []);
47
+ }
48
+
49
+ const showView = ref(false);
50
+ </script>
51
+
52
+ <template>
53
+ <q-field v-bind="attrs" :loading="loading" :model-value="localData" @update:model-value="onClear" :clearable="hasFile">
54
+ <template v-slot:control>
55
+ <template v-if="!hasFile">
56
+ <input type="file" accept=".xlsx" @change="onChange" ref="file" />
57
+ </template>
58
+
59
+ <template v-else>
60
+ {{ localData.length }} records
61
+ </template>
62
+
63
+ </template>
64
+
65
+ <template v-slot:prepend v-if="hasFile">
66
+ <q-icon name="sym_o_table_view" @click="showView = true" class="cursor-pointer" />
67
+ </template>
68
+
69
+ <q-dialog v-model="showView" full-width>
70
+ <q-card>
71
+ <q-card-section>
72
+ <q-table :rows="localData" />
73
+ </q-card-section>
74
+ </q-card>
75
+ </q-dialog>
76
+
77
+ </q-field>
78
+ </template>
@@ -1,9 +1,9 @@
1
1
  <script setup>
2
2
  import { useLight } from "#imports";
3
3
  import { ref, reactive, onMounted } from 'vue'
4
- import { useQuasar, Notify } from 'quasar';
4
+ import { useQuasar, Notify, Dialog } from 'quasar';
5
5
  import { useI18n } from 'vue-i18n';
6
- import { m, notify } from '../';
6
+ import { m, notify } from '#imports';
7
7
 
8
8
  import { login, webauthnLogin } from '@hostlink/light';
9
9
 
@@ -35,9 +35,7 @@ const submit = async () => {
35
35
  }
36
36
 
37
37
  const forgetPassword = async () => {
38
-
39
- //show dialog
40
- qua.dialog({
38
+ Dialog.create({
41
39
  title: i18n.t("Forget password"),
42
40
  message: "Please enter your email address, we will send you a code to reset your password",
43
41
  prompt: {
@@ -50,9 +48,9 @@ const forgetPassword = async () => {
50
48
  return;
51
49
  }
52
50
 
53
-
54
- if (await m("forgetPassword", { email: email })) {
55
- qua.dialog({
51
+ try {
52
+ await m("forgetPassword", { email: email });
53
+ Dialog.create({
56
54
  title: "Enter your code",
57
55
  message: "Please enter the code sent to your email, your code will expire in 10 minutes",
58
56
  prompt: {
@@ -67,7 +65,7 @@ const forgetPassword = async () => {
67
65
  }
68
66
 
69
67
  if (await m("verifyCode", { code: code, email: email })) {
70
- qua.dialog({
68
+ Dialog.create({
71
69
  title: "Reset password",
72
70
  message: "Please enter your new password",
73
71
  prompt: {
@@ -101,9 +99,19 @@ const forgetPassword = async () => {
101
99
  }
102
100
 
103
101
 
102
+ });
103
+ } catch (e) {
104
+ qua.notify({
105
+ message: e.message,
106
+ color: "negative",
107
+ icon: "sym_o_error",
108
+ position: "top",
109
+ timeout: 2000
104
110
  });
105
111
  }
106
112
  });
113
+
114
+
107
115
  };
108
116
 
109
117
  const hasBioLogin = ref(false);
@@ -92,6 +92,12 @@ useHead({
92
92
 
93
93
  <template>
94
94
  <q-page padding>
95
+ <!-- q-breadcrumbs>
96
+ <q-breadcrumbs-el icon="home" to="/" />
97
+ <q-breadcrumbs-el label="Permission" to="/Permission" />
98
+ <q-breadcrumbs-el label="Build" />
99
+ </q-breadcrumbs -->
100
+
95
101
  <q-toolbar v-if="showToolbar">
96
102
  <l-back-btn v-if="showBackBtn" />
97
103
  <l-add-btn v-if="showAddBtn" />
@@ -100,6 +106,7 @@ useHead({
100
106
  <q-toolbar-title>{{ $t(title) }}</q-toolbar-title>
101
107
  </q-toolbar>
102
108
 
109
+
103
110
  <div class="q-gutter-sm q-mb-sm">
104
111
  <slot name="header"></slot>
105
112
  </div>
@@ -0,0 +1,51 @@
1
+ <script setup>
2
+ import { computed, useSlots } from 'vue'
3
+ import { getErrorMessage } from 'formkit-quasar';
4
+ import { useLight } from '#imports';
5
+
6
+
7
+ const props = defineProps({
8
+ context: Object
9
+ });
10
+
11
+ const { error, errorMessage } = getErrorMessage(props.context.node);
12
+
13
+ const value = computed({
14
+ get: () => props.context.value,
15
+ set: (val) => props.context.node.input(val)
16
+ })
17
+
18
+ const ss = Object.entries(useSlots()).map(([key]) => key);
19
+
20
+ const onBlur = () => {
21
+ if (errorMessage.value) {
22
+ error.value = true
23
+ } else {
24
+ error.value = false
25
+ }
26
+ }
27
+
28
+ const light = useLight();
29
+
30
+ const attrs = {
31
+ ...{
32
+ filled: light.getStyle("inputFilled"),
33
+ outlined: light.getStyle("inputOutlined"),
34
+ standout: light.getStyle("inputStandout"),
35
+ rounded: light.getStyle("inputRounded"),
36
+ dense: light.getStyle("inputDense"),
37
+ square: light.getStyle("inputSquare"),
38
+ stackLabel: light.getStyle("inputStackLabel")
39
+ },
40
+ ...props.context.attrs
41
+ }
42
+
43
+ </script>
44
+ <template>
45
+ <q-file v-model="value" :label="context.label" v-bind="attrs" :error="error" :error-message="errorMessage"
46
+ @blur="onBlur">
47
+ <template v-for="s in ss" v-slot:[s]="props" :key="s">
48
+ <slot :name="s" v-bind="props ?? {}"></slot>
49
+ </template>
50
+ </q-file>
51
+ </template>
@@ -0,0 +1,22 @@
1
+ <script setup>
2
+ import { computed } from 'vue'
3
+ import { getErrorMessage } from 'formkit-quasar';
4
+
5
+ const props = defineProps({
6
+ context: Object
7
+ });
8
+
9
+ const { error, errorMessage } = getErrorMessage(props.context.node);
10
+
11
+ const value = computed({
12
+ get: () => props.context.value,
13
+ set: (val) => props.context.node.input(val)
14
+ })
15
+
16
+ </script>
17
+ <template>
18
+ <l-input-xlsx v-model="value" :label="context.label" v-bind="context.attrs" :error="error" :type="context.inputType"
19
+ :error-message="errorMessage">
20
+ <slot></slot>
21
+ </l-input-xlsx>
22
+ </template>
@@ -0,0 +1,18 @@
1
+ <script setup>
2
+ import { computed } from 'vue'
3
+
4
+ const props = defineProps({
5
+ context: Object
6
+ });
7
+
8
+ const value = computed({
9
+ get: () => props.context.value,
10
+ set: (val) => props.context.node.input(val)
11
+ })
12
+
13
+
14
+ </script>
15
+ <template>
16
+ <q-toggle v-model="value" :label="context.label" v-bind="context.attrs">
17
+ </q-toggle>
18
+ </template>
@@ -9,10 +9,22 @@ import DatePickerVue from "./DatePicker.vue";
9
9
  import TimePickerVue from "./TimePicker.vue";
10
10
  import OptionGroupVue from "./OptionGroup.vue";
11
11
  import FilePickerVue from "./FilePicker.vue";
12
+ import FileVue from "./File.vue";
13
+ import InputXlsxVue from "./InputXlsx.vue";
12
14
  export const createLightPlugin = () => {
13
15
  return (node) => {
14
16
  let type = node.props.type + "";
15
17
  switch (type) {
18
+ case "l-input-xlsx":
19
+ return node.define({
20
+ type: "input",
21
+ component: InputXlsxVue
22
+ });
23
+ case "l-file":
24
+ return node.define({
25
+ type: "input",
26
+ component: FileVue
27
+ });
16
28
  case "l-file-picker":
17
29
  return node.define({
18
30
  type: "input",
@@ -18,7 +18,7 @@ import getModelFields from "./getModelFields.mjs";
18
18
  import getModelColumns from "./getModelColumns.mjs";
19
19
  import sv from "./sv.mjs";
20
20
  import model from "./model.mjs";
21
- const notify = function(message, color = "green") {
21
+ const notify = function(message, color = "positive") {
22
22
  Notify.create({
23
23
  message,
24
24
  color,
@@ -14,10 +14,10 @@ const { system: { database } } = await query({
14
14
  <template>
15
15
  <l-page>
16
16
  <q-card flat bordered>
17
- <q-list bordered class="rounded-borders" separator>
17
+ <q-list bordered class="rounded-borders" separator>
18
18
  <q-expansion-item :label="table.name" v-for="table in database.table" expand-separator>
19
19
  <div class=" q-ma-sm">
20
- <q-table :rows="table.columns" :rows-per-page-options="[0]" hide-pagination flat bordered></q-table>
20
+ <q-table dense :rows="table.columns" :rows-per-page-options="[0]" hide-pagination flat bordered></q-table>
21
21
  </div>
22
22
 
23
23
  </q-expansion-item>
@@ -1,24 +1,34 @@
1
1
  <script setup>
2
- import { reactive } from 'vue'
3
2
  import { m } from '../../'
4
- const obj = reactive({})
3
+ import { Notify } from "quasar"
5
4
 
6
- const onSave = async () => {
7
- if (await m("mailTest", obj)) {
8
- que.notify({
9
- message: "Mail sent",
5
+ const onSubmit = async (data) => {
6
+ try {
7
+ await m("mailTest", data)
8
+
9
+
10
+ Notify.create({
11
+ message: "Email sent",
10
12
  color: "positive",
11
- icon: "email"
13
+ icon: "check"
12
14
  });
15
+
16
+ } catch (e) {
17
+ Notify.create({
18
+ message: e.message,
19
+ color: "negative",
20
+ icon: "warning"
21
+ })
13
22
  }
14
23
  }
15
24
  </script>
16
25
  <template>
17
26
  <l-page>
18
- <l-form @save="onSave">
19
- <l-input label="Email" v-model="obj.email" required></l-input>
20
- <l-input label="Subject" v-model="obj.subject" required></l-input>
21
- <l-input type="textarea" label="Content" v-model="obj.content" required></l-input>
22
- </l-form>
27
+ <FormKit type="l-form" @submit="onSubmit">
28
+ <FormKit type="l-input" label="Email" name="email" validation="required|email"></FormKit>
29
+ <FormKit type="l-input" label="Subject" name="subject" validation="required"></FormKit>
30
+ <FormKit type="l-input" input-type="textarea" label="Content" name="content" validation="required">
31
+ </FormKit>
32
+ </FormKit>
23
33
  </l-page>
24
34
  </template>
@@ -274,10 +274,15 @@ const onAddChildMenu = (node, type) => {
274
274
  <div class="q-gutter-md">
275
275
  <l-input label="Label" v-model="selectedNode.label" />
276
276
  <l-input label="To" v-model="selectedNode.to" />
277
- <l-input label="Icon" v-model="selectedNode.icon" hint="example: sym_o_add"/>
277
+ <l-input label="Icon" v-model="selectedNode.icon" hint="example: sym_o_add" />
278
278
  <l-input label="Permission" v-model="selectedNode.permission" />
279
279
  <l-input label="UUID" v-model="selectedNode.uuid" readonly />
280
280
  </div>
281
+
282
+ <div>
283
+ <a href="https://fonts.google.com/icons" target="_blank">Material Icons</a>
284
+ </div>
285
+
281
286
  </q-card-section>
282
287
  </template>
283
288
 
@@ -1,9 +1,9 @@
1
1
  <script setup>
2
- import { q } from '../../'
2
+ import { q } from '#imports'
3
3
  const { system } = await q({ system: ["package"] })
4
4
  </script>
5
5
  <template>
6
6
  <l-page>
7
- <q-table :rows="system.package" :rows-per-page-options="[0]" hide-pagination flat bordered></q-table>
7
+ <q-table dense :rows="system.package" :rows-per-page-options="[0]" hide-pagination flat bordered></q-table>
8
8
  </l-page>
9
9
  </template>
@@ -1,23 +1,24 @@
1
1
  <script setup>
2
- import { useQuasar } from 'quasar'
2
+ import { Notify } from 'quasar'
3
3
  import { useRouter } from 'vue-router'
4
- import { reactive } from "vue"
5
4
  import { q, m } from '../../'
6
5
 
7
6
  const router = useRouter()
8
- const que = useQuasar()
9
-
10
7
  const { app } = await q({ app: { config: ["name", "value"] } })
11
-
12
- //const system = await q("system", ["passwordPolicy"])
13
-
14
- const obj = reactive(app.config.reduce((acc, cur) => {
8
+ const obj = app.config.reduce((acc, cur) => {
15
9
  acc[cur.name] = cur.value
16
10
  return acc
17
- }, {}));
18
-
19
- const fields = ["company", "company_logo", "password_upper_case", "password_lower_case", "password_number", "password_special_character",
20
- "file_manager", "two_factor_authentication"];
11
+ }, {});
12
+
13
+ const fields = ["company", "company_logo",
14
+ "password_contains_uppercase",
15
+ "password_contains_lowercase",
16
+ "password_contains_numeric",
17
+ "password_contains_symbol",
18
+ "password_min_length",
19
+ "file_manager",
20
+ "two_factor_authentication",
21
+ "mode"];
21
22
 
22
23
  //filter out fields that are not in the app.config table
23
24
  Object.keys(obj).forEach((key) => {
@@ -26,53 +27,72 @@ Object.keys(obj).forEach((key) => {
26
27
  }
27
28
  })
28
29
 
29
- const onSave = async () => {
30
- const data = [];
31
- Object.keys(obj).forEach((key) => {
30
+ const onSubmit = async (d, form) => {
31
+ let data = [];
32
+ Object.keys(d).forEach((key) => {
32
33
  data.push({
33
34
  name: key,
34
- value: obj[key]
35
+ value: d[key]
35
36
  })
36
37
  })
38
+
37
39
  await m("updateAppConfigs", { data })
38
40
  //show success
39
- que.notify({
40
- message: "Saved",
41
- type: "positive",
42
- position: "top",
43
- });
41
+ Notify.create({
42
+ message: "Settings updated",
43
+ color: "positive",
44
+ icon: "check"
45
+ })
46
+ router.go(-1);
47
+ }
44
48
 
45
49
 
50
+ if (obj.mode != 'prod') {
51
+ obj.mode = 'dev'
52
+ } else {
53
+ obj.mode = 'prod'
46
54
  }
47
55
 
48
56
 
49
57
  </script>
50
58
  <template>
51
59
  <l-page>
52
- <l-form @save="onSave">
53
- <l-input label="Company" v-model="obj.company"></l-input>
54
- <l-input label="Company logo" v-model="obj.company_logo"></l-input>
60
+
61
+ <FormKit type="l-form" :value="obj" #default="{ value }" @submit="onSubmit">
62
+ <FormKit type="l-input" label="Company" name="company" validation="required"></FormKit>
63
+ <FormKit type="l-input" label="Company logo" name="company_logo"></FormKit>
55
64
 
56
65
  <q-field label="Password policy" stack-label>
57
- <q-checkbox label="Upper case" v-model="obj.password_upper_case" true-value="1"
58
- false-value="0"></q-checkbox>
59
- <q-checkbox label="Lower case" v-model="obj.password_lower_case" true-value="1"
60
- false-value="0"></q-checkbox>
61
- <q-checkbox label="Number" v-model="obj.password_number" true-value="1" false-value="0"></q-checkbox>
62
- <q-checkbox label="Special character" v-model="obj.password_special_character" true-value="1"
63
- false-value="0"></q-checkbox>
66
+ <FormKit type="q-checkbox" label="Upper Case" name="password_contains_uppercase" true-value="1"
67
+ false-value="0" />
68
+ <FormKit type="q-checkbox" label="Lower Case" name="password_contains_lowercase" true-value="1"
69
+ false-value="0" />
70
+ <FormKit type="q-checkbox" label="Number" name="password_contains_numeric" true-value="1" false-value="0" />
71
+ <FormKit type="q-checkbox" label="Special Character" name="password_contains_symbol" true-value="1"
72
+ false-value="0" />
64
73
  </q-field>
65
74
 
75
+ <FormKit type="l-input" label="Password min Length" name="password_min_length" validation="required|number">
76
+ </FormKit>
66
77
 
67
78
  <q-field label="File manager" stack-label>
68
- <q-checkbox label="Show" v-model="obj.file_manager" true-value="1" false-value="0"></q-checkbox>
79
+ <FormKit type="q-checkbox" label="Show" name="file_manager" true-value="1" false-value="0" />
69
80
  </q-field>
70
81
 
71
82
  <q-field label="Two factor authentication" stack-label>
72
- <q-checkbox label="Enable" v-model="obj.two_factor_authentication" true-value="1"
73
- false-value="0"></q-checkbox>
83
+ <FormKit type="q-checkbox" label="Enable" name="two_factor_authentication" true-value="1" false-value="0" />
74
84
  </q-field>
75
85
 
76
- </l-form>
86
+ <FormKit label="Mode" type="l-select" :options="[
87
+ { label: 'Production', value: 'prod' },
88
+ { label: 'Development', value: 'dev' },
89
+
90
+ ]" name="mode" validation="required">
91
+
92
+
93
+ </FormKit>
94
+
95
+
96
+ </FormKit>
77
97
  </l-page>
78
98
  </template>
package/dist/types.d.mts CHANGED
@@ -1,5 +1,5 @@
1
1
 
2
- import type { ModuleOptions } from './module'
2
+ import type { ModuleOptions } from './module.js'
3
3
 
4
4
 
5
5
  declare module '@nuxt/schema' {
@@ -13,4 +13,4 @@ declare module 'nuxt/schema' {
13
13
  }
14
14
 
15
15
 
16
- export type { ModuleOptions, default } from './module'
16
+ export type { ModuleOptions, default } from './module.js'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hostlink/nuxt-light",
3
- "version": "0.0.118",
3
+ "version": "1.0.2",
4
4
  "description": "HostLink Nuxt Light Framework",
5
5
  "repository": "@hostlink/nuxt-light",
6
6
  "license": "MIT",
@@ -39,15 +39,13 @@
39
39
  "@nuxt/module-builder": "^0.5.2",
40
40
  "@quasar/extras": "^1.16.6",
41
41
  "axios": "^1.5.0",
42
- "formkit-quasar": "^0.0.12",
42
+ "formkit-quasar": "^0.0.14",
43
43
  "json-to-graphql-query": "^2.2.5",
44
44
  "quasar": "^2.12.5",
45
- "unplugin-auto-import": "^0.16.6",
46
45
  "vue-i18n": "^9.2.2",
47
46
  "xlsx": "^0.18.5"
48
47
  },
49
48
  "devDependencies": {
50
- "@nuxt/content": "^2.8.2",
51
49
  "@nuxt/devtools": "latest",
52
50
  "@nuxt/eslint-config": "^0.2.0",
53
51
  "@nuxt/schema": "^3.7.4",