contactstudiocstools 1.0.224

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 (91) hide show
  1. package/README.md +94 -0
  2. package/dist/module.cjs +5 -0
  3. package/dist/module.d.ts +7 -0
  4. package/dist/module.json +5 -0
  5. package/dist/module.mjs +72 -0
  6. package/dist/runtime/components/Atom.Alert.vue +46 -0
  7. package/dist/runtime/components/Atom.Auth.vue +37 -0
  8. package/dist/runtime/components/Atom.BannerChatEmpty.vue +18 -0
  9. package/dist/runtime/components/Atom.BannerPage404.vue +28 -0
  10. package/dist/runtime/components/Atom.BannerPageUnauthorized.vue +18 -0
  11. package/dist/runtime/components/Atom.Breadcrumb.vue +26 -0
  12. package/dist/runtime/components/Atom.ChatContact.vue +136 -0
  13. package/dist/runtime/components/Atom.ChatContactSchedule.vue +87 -0
  14. package/dist/runtime/components/Atom.ChatMessageFooter.vue +25 -0
  15. package/dist/runtime/components/Atom.DarkMode.vue +67 -0
  16. package/dist/runtime/components/Atom.DraggableWindow.vue +102 -0
  17. package/dist/runtime/components/Atom.Dropdown.vue +9 -0
  18. package/dist/runtime/components/Atom.DropdownSearchable.vue +25 -0
  19. package/dist/runtime/components/Atom.Fetch.vue +46 -0
  20. package/dist/runtime/components/Atom.Field.vue +43 -0
  21. package/dist/runtime/components/Atom.FieldDate.vue +19 -0
  22. package/dist/runtime/components/Atom.FieldNumber.vue +19 -0
  23. package/dist/runtime/components/Atom.FieldPhone.vue +92 -0
  24. package/dist/runtime/components/Atom.FieldSelect.vue +28 -0
  25. package/dist/runtime/components/Atom.FieldSelectMultiple.vue +49 -0
  26. package/dist/runtime/components/Atom.FieldText.vue +19 -0
  27. package/dist/runtime/components/Atom.FieldTextarea.vue +41 -0
  28. package/dist/runtime/components/Atom.Loading.vue +80 -0
  29. package/dist/runtime/components/Atom.Notification.vue +48 -0
  30. package/dist/runtime/components/Atom.Ringtone.vue +23 -0
  31. package/dist/runtime/components/Atom.SelectTreeField.vue +49 -0
  32. package/dist/runtime/components/Atom.Snapshot.vue +33 -0
  33. package/dist/runtime/components/Atom.Tabs.vue +60 -0
  34. package/dist/runtime/components/Molecule.ChatMessageFile.vue +102 -0
  35. package/dist/runtime/components/Molecule.ChatMessageOption.vue +85 -0
  36. package/dist/runtime/components/Molecule.ChatMessageText.vue +36 -0
  37. package/dist/runtime/components/Molecule.ClientHistory.vue +62 -0
  38. package/dist/runtime/components/Molecule.DropdownDDI.vue +333 -0
  39. package/dist/runtime/components/Molecule.FieldGroup.vue +73 -0
  40. package/dist/runtime/components/Molecule.FieldSelectMultiple.vue +19 -0
  41. package/dist/runtime/components/Molecule.File.vue +84 -0
  42. package/dist/runtime/components/Molecule.SelectTreeSearchable.vue +126 -0
  43. package/dist/runtime/components/Molecule.Status.vue +154 -0
  44. package/dist/runtime/components/Molecule.TimeDaily.vue +9 -0
  45. package/dist/runtime/components/Organism.Attachments.vue +139 -0
  46. package/dist/runtime/components/Organism.ChatMessages.vue +31 -0
  47. package/dist/runtime/components/Organism.ChatRoom.vue +342 -0
  48. package/dist/runtime/components/Organism.ChatSchedule.vue +110 -0
  49. package/dist/runtime/components/Organism.ClientHistoryTable.vue +85 -0
  50. package/dist/runtime/components/Organism.ClientHistoryTimeline.vue +77 -0
  51. package/dist/runtime/components/Organism.FAQ.vue +88 -0
  52. package/dist/runtime/components/Organism.Form.vue +67 -0
  53. package/dist/runtime/components/Organism.FormMailing.vue +112 -0
  54. package/dist/runtime/components/Organism.HeaderMain.vue +79 -0
  55. package/dist/runtime/components/Organism.Manifestation.vue +146 -0
  56. package/dist/runtime/components/Organism.Nav.vue +27 -0
  57. package/dist/runtime/components/Organism.NavMain.vue +187 -0
  58. package/dist/runtime/components/Organism.PageContainer.vue +22 -0
  59. package/dist/runtime/components/Organism.Schedule.vue +170 -0
  60. package/dist/runtime/components/Organism.Tabulation.vue +237 -0
  61. package/dist/runtime/components/types/dto.d.ts +16 -0
  62. package/dist/runtime/components/types/dto.mjs +236 -0
  63. package/dist/runtime/components/types/helpers.d.ts +39 -0
  64. package/dist/runtime/components/types/helpers.mjs +295 -0
  65. package/dist/runtime/components/types/index.d.ts +4 -0
  66. package/dist/runtime/components/types/index.mjs +4 -0
  67. package/dist/runtime/components/types/types.d.ts +198 -0
  68. package/dist/runtime/components/types/types.mjs +35 -0
  69. package/dist/runtime/index.css +1 -0
  70. package/dist/runtime/plugins/clickOutside.d.ts +2 -0
  71. package/dist/runtime/plugins/clickOutside.mjs +16 -0
  72. package/dist/runtime/plugins/emitter.d.ts +2 -0
  73. package/dist/runtime/plugins/emitter.mjs +17 -0
  74. package/dist/runtime/public/192x192.png +0 -0
  75. package/dist/runtime/public/404.svg +1 -0
  76. package/dist/runtime/public/512x512.png +0 -0
  77. package/dist/runtime/public/chat.svg +138 -0
  78. package/dist/runtime/public/chatbg.png +0 -0
  79. package/dist/runtime/public/dev-sw.d.ts +0 -0
  80. package/dist/runtime/public/dev-sw.mjs +0 -0
  81. package/dist/runtime/public/empty.svg +1 -0
  82. package/dist/runtime/public/loading.svg +1 -0
  83. package/dist/runtime/public/messages.svg +1 -0
  84. package/dist/runtime/public/privacy.svg +1 -0
  85. package/dist/runtime/public/ringtone.mp3 +0 -0
  86. package/dist/runtime/public/security.svg +188 -0
  87. package/dist/runtime/public/snapshot.d.ts +15 -0
  88. package/dist/runtime/public/snapshot.mjs +77 -0
  89. package/dist/runtime/public/unauthorized.svg +1 -0
  90. package/dist/types.d.ts +10 -0
  91. package/package.json +50 -0
@@ -0,0 +1,110 @@
1
+ <template>
2
+ <div>
3
+ <header class="w-full h-12 flex items-center justify-between px-3">
4
+ <slot />
5
+ </header>
6
+
7
+ <div class="divider" />
8
+
9
+ <div class="flex items-center text-xs opacity-75 py-2 px-4">
10
+ <span class="w-full">
11
+ {{ visibleSchedules ? "Convites Enviados" : "Conversas" }}
12
+ </span>
13
+ <span
14
+ v-if="!visibleSchedules"
15
+ class="text-primary flex items-center gap-2 cursor-pointer whitespace-nowrap"
16
+ @click="changeSchedule"
17
+ >
18
+ Convites Enviados
19
+ <i class="bi-chevron-right leading-3 " />
20
+ </span>
21
+ <span
22
+ v-if="visibleSchedules"
23
+ class="text-error flex items-center gap-2 cursor-pointer"
24
+ @click="changeSchedule"
25
+ >
26
+ <i class="bi-chevron-left leading-3" />
27
+ Voltar
28
+ </span>
29
+ </div>
30
+
31
+ <div class="divider" />
32
+
33
+ <article class="overflow-auto max-h-full scrollbar pb-24">
34
+ <!-- contacts -->
35
+ <div
36
+ v-for="(contact, i) in contacts"
37
+ v-show="!visibleSchedules"
38
+ :key="i"
39
+ >
40
+ <ul class="list">
41
+ <AtomChatContact
42
+ :timeout="timeout"
43
+ :contact="contact"
44
+ :class="getSelectedClass(contact)"
45
+ @click="select(contact)"
46
+ />
47
+ </ul>
48
+ <div class="divider" />
49
+ </div>
50
+
51
+ <!-- schedules -->
52
+ <div
53
+ v-for="(schedule, i) in schedules"
54
+ v-show="visibleSchedules"
55
+ :key="i"
56
+ >
57
+ <ul class="list my-2">
58
+ <AtomChatContactSchedule :schedule="schedule" />
59
+ </ul>
60
+ <div class="divider" />
61
+ </div>
62
+ </article>
63
+ </div>
64
+ </template>
65
+
66
+ <script setup lang="ts">
67
+ import { ref, computed } from "vue";
68
+ import { IChatContact, IChatContacts, IPortfolioSchedules } from "./types";
69
+
70
+ // props
71
+ interface IProps {
72
+ contacts: IChatContacts;
73
+ schedules: IPortfolioSchedules;
74
+ selected?: IChatContact;
75
+ timeout: number;
76
+ }
77
+ const props = defineProps<IProps>();
78
+
79
+ // emits
80
+ interface IEmits {
81
+ (e: "select", contact: IChatContact): void;
82
+ (e: "schedule", schedule: boolean): void;
83
+ }
84
+ const emit = defineEmits<IEmits>();
85
+
86
+ // computed
87
+ const getSelectedClass = computed<Function>(() => (contact: IChatContact) => {
88
+ if (props.selected?.id === contact.id)
89
+ return "bg-slate-100 dark:bg-slate-800";
90
+ return "";
91
+ });
92
+
93
+ // data
94
+ const visibleSchedules = ref<boolean>(false)
95
+
96
+ // methods
97
+ function select(contact: IChatContact): void {
98
+ emit("select", contact);
99
+ }
100
+ function changeSchedule(): void {
101
+ visibleSchedules.value = !visibleSchedules.value
102
+ emit("schedule", visibleSchedules.value);
103
+ }
104
+ </script>
105
+
106
+ <style scoped>
107
+ .text-2xs {
108
+ font-size: 0.7rem;
109
+ }
110
+ </style>
@@ -0,0 +1,85 @@
1
+ <template>
2
+ <div class="w-full h-full overflow-auto scrollbar">
3
+ <table class="table bordered text-xs">
4
+ <thead class="bg-slate-100 dark:bg-slate-800">
5
+ <th />
6
+ <th>Canal</th>
7
+ <th>Operador</th>
8
+ <th>Data</th>
9
+ <th>Tabulação</th>
10
+ <th>Observação</th>
11
+ <slot name="header" />
12
+ </thead>
13
+ <tbody>
14
+ <tr
15
+ v-for="(history, i) in histories"
16
+ :key="i"
17
+ >
18
+ <td>
19
+ <i
20
+ v-if="isDigital(history)"
21
+ class="bi bi-eye-fill text-slate-500 px-1 text-base leading-3 cursor-pointer"
22
+ @click="emit('extract', history)"
23
+ />
24
+ </td>
25
+ <td>
26
+ <span
27
+ v-if="isDigital(history)"
28
+ class="badge badge-solid-primary"
29
+ >
30
+ <i class="bi-chat-square-dots-fill" />
31
+ <b class="ml-1">Digital</b>
32
+ </span>
33
+ <span
34
+ v-else
35
+ class="badge badge-solid-primary"
36
+ >
37
+ <i class="bi-telephone-fill" />
38
+ <b class="ml-1">Outro</b>
39
+ </span>
40
+ </td>
41
+ <td>{{ history.agent }}</td>
42
+ <td>{{ history.lastMovementDate?.toLocaleString() }}</td>
43
+ <td>{{ history.lastTabulation }}</td>
44
+ <td class="!whitespace-normal">
45
+ {{ history.observation ?? "" }}
46
+ </td>
47
+ <slot
48
+ name="body"
49
+ :item="history"
50
+ />
51
+ </tr>
52
+ </tbody>
53
+ </table>
54
+ </div>
55
+ </template>
56
+
57
+ <script setup lang="ts">
58
+ import { computed } from "vue";
59
+ import { IClientHistories, IClientHistory } from "./types";
60
+
61
+ // props
62
+ interface IProps {
63
+ histories: IClientHistories;
64
+ }
65
+ defineProps<IProps>();
66
+
67
+ // computed
68
+ const isDigital = computed<any>(() => ({ primitive }: any) => {
69
+ return !!primitive.room_id && primitive.room_id !== ""
70
+ });
71
+
72
+ // emits
73
+ interface IEmits {
74
+ (e: "extract", history: IClientHistory): void;
75
+ }
76
+ const emit = defineEmits<IEmits>();
77
+ </script>
78
+
79
+ <style scoped>
80
+ .table th,
81
+ .table td {
82
+ padding: 3px 10px;
83
+ white-space: nowrap;
84
+ }
85
+ </style>
@@ -0,0 +1,77 @@
1
+ <template>
2
+ <figure class="timeline">
3
+ <figcaption
4
+ v-for="(history, i) in histories"
5
+ :key="i"
6
+ class="checkpoint checkpoint-solid-primary !pr-0"
7
+ :icon="isDigital(history) ? `&#xF25E;` : `&#xF5B4;`"
8
+ >
9
+ <!-- header -->
10
+ <header class="flex items-center justify-between">
11
+ <span class="font-medium">
12
+ {{ history.agent }}
13
+ <i
14
+ v-if="isDigital(history)"
15
+ class="bi bi-eye-fill text-slate-500 px-1 text-base leading-3 cursor-pointer"
16
+ @click="emit('extract', history)"
17
+ />
18
+ </span>
19
+
20
+ <span
21
+ v-if="history.lastTabulation"
22
+ class="badge badge-soft-secondary font-medium w-fit"
23
+ > {{ history.lastTabulation }}</span>
24
+ </header>
25
+
26
+ <!-- last date -->
27
+ <span
28
+ v-if="history.lastMovementDate"
29
+ class="text-xs opacity-50"
30
+ >
31
+ {{ history.lastMovementDate.toLocaleString() }}
32
+ </span>
33
+
34
+ <article
35
+ v-if="history.observation"
36
+ class="accordion accordion-bordered mt-2"
37
+ >
38
+ <details class="item">
39
+ <summary class="text-xs !py-2 !px-4">
40
+ Observação
41
+ </summary>
42
+ <article class="text-xs !py-2 !px-4">
43
+ {{ history.observation }}
44
+ </article>
45
+ </details>
46
+ </article>
47
+ <slot
48
+ name="body"
49
+ :item="history"
50
+ />
51
+ </figcaption>
52
+ </figure>
53
+ </template>
54
+
55
+ <script setup lang="ts">
56
+ import { computed } from "vue"
57
+ import { IClientHistories, IClientHistory } from "./types";
58
+
59
+ // props
60
+ interface IProps {
61
+ histories: IClientHistories;
62
+ }
63
+ defineProps<IProps>();
64
+
65
+ // computed
66
+ const isDigital = computed<any>(() => ({ primitive }: any) => {
67
+ return !!primitive.room_id && primitive.room_id !== ""
68
+ })
69
+
70
+ // emits
71
+ interface IEmits {
72
+ (e: "extract", history: IClientHistory): void;
73
+ }
74
+ const emit = defineEmits<IEmits>();
75
+ </script>
76
+
77
+ <style scoped></style>
@@ -0,0 +1,88 @@
1
+ <template>
2
+ <aside class="relative">
3
+ <!-- field -->
4
+ <label class="field-group sticky top-0 z-10 bg-white dark:bg-slate-900">
5
+ <input
6
+ v-model="search"
7
+ type="text"
8
+ placeholder="Pesquise"
9
+ class="input sidenav-item"
10
+ >
11
+ <i class="bi-search addon w-10" />
12
+ </label>
13
+ <!-- accordion -->
14
+ <div class="accordion markdown">
15
+ <details
16
+ v-for="({ answer, question, primitive }, i) in getDoubtsBySearch"
17
+ :key="i"
18
+ class="item"
19
+ >
20
+ <summary v-html="question" />
21
+ <article class="flex">
22
+ <div
23
+ class="flex-1"
24
+ v-html="answer"
25
+ />
26
+ <button
27
+ class="btn btn-outline-secondary max-h-none ml-2"
28
+ @click="copy(primitive.answer)"
29
+ >
30
+ <i class="bi bi-clipboard" />
31
+ </button>
32
+ </article>
33
+ </details>
34
+ </div>
35
+ </aside>
36
+ </template>
37
+
38
+ <script setup lang="ts">
39
+ import { marked } from "marked";
40
+ import { ref, onMounted, computed } from "vue";
41
+ import { IDoubts } from "./types";
42
+ import { useNuxtApp } from "#app";
43
+
44
+ // props
45
+ interface IProps {
46
+ doubts: IDoubts;
47
+ }
48
+ const props = defineProps<IProps>();
49
+
50
+ // computed
51
+ const getDoubtsBySearch = computed<IDoubts>(() => {
52
+ if (!search.value.trim()) return props.doubts;
53
+
54
+ return props.doubts.filter(({ question, answer }) => {
55
+ return (
56
+ question.match(new RegExp(search.value, "i")) ||
57
+ answer.match(new RegExp(search.value, "i"))
58
+ );
59
+ });
60
+ });
61
+
62
+ // data
63
+ const { $emit } = useNuxtApp();
64
+ const search = ref<string>("");
65
+
66
+ // methods
67
+ function init(): void {
68
+ for (const doubt of props.doubts) {
69
+ const { question, answer } = doubt;
70
+ doubt.primitive = {
71
+ question,
72
+ answer,
73
+ };
74
+ doubt.question = marked(question);
75
+ doubt.answer = marked(answer);
76
+ }
77
+ }
78
+ async function copy(answer: string): Promise<void> {
79
+ $emit("faq:copy", answer);
80
+ }
81
+
82
+ // mounted
83
+ onMounted(() => {
84
+ init();
85
+ });
86
+ </script>
87
+
88
+ <style></style>
@@ -0,0 +1,67 @@
1
+ <template>
2
+ <form
3
+ class="form"
4
+ @submit.prevent="submit"
5
+ >
6
+ <slot
7
+ :get-ref="getRef"
8
+ :models="models"
9
+ />
10
+ </form>
11
+ </template>
12
+
13
+ <script setup lang="ts">
14
+ import { reactive } from "vue";
15
+ import { useNuxtApp } from "#app";
16
+
17
+ interface IProps {
18
+ submit: Function;
19
+ deny?: Function;
20
+ defaults?: { [name: string]: string };
21
+ }
22
+ const props = defineProps<IProps>();
23
+
24
+ // data
25
+ const { $emit } = useNuxtApp()
26
+ const models = reactive<any>({ ...props.defaults });
27
+ const fields = reactive<any[]>([]);
28
+
29
+ // methods
30
+ function getRef(ref: any): void {
31
+ fields.push(ref);
32
+ }
33
+ async function submit(): Promise<void> {
34
+ if (!validate()) {
35
+ await deny();
36
+ return;
37
+ }
38
+
39
+ await props.submit(models);
40
+ }
41
+ function validate(): boolean {
42
+ let valid = true;
43
+
44
+ for (const field of fields) {
45
+ if (!field.validateAndSetState()) {
46
+ valid = false;
47
+ }
48
+ }
49
+
50
+ return valid;
51
+ }
52
+ async function deny(): Promise<void> {
53
+ if (props.deny) await props.deny();
54
+ }
55
+ function append(values: any): void {
56
+ Object.assign(models, values);
57
+ $emit("form:append", models)
58
+ }
59
+ function reset(): void {
60
+ for (const field in models) delete models[field];
61
+ }
62
+
63
+ // expose
64
+ defineExpose({ submit, append, reset, validate });
65
+ </script>
66
+
67
+ <style scoped></style>
@@ -0,0 +1,112 @@
1
+ <template>
2
+ <OrganismForm
3
+ v-slot="{ models, getRef }"
4
+ ref="form"
5
+ :submit="submit"
6
+ :deny="deny"
7
+ :defaults="defaults"
8
+ class="form"
9
+ >
10
+ <div
11
+ v-for="(field, i) in mailing"
12
+ :key="i"
13
+ class="flex-auto"
14
+ :disabled="field.disabled"
15
+ >
16
+ <MoleculeFieldGroup
17
+ :ref="getRef"
18
+ :label="field.label"
19
+ :rules="[getRulesByField(models[field.name], field)]"
20
+ >
21
+ <!-- ddi -->
22
+ <!-- <MoleculeDropdownDDI
23
+ v-if="field.type === EFieldTypes.phone"
24
+ v-model="models[field.name + '_DDI']"
25
+ :phone="models[field.name]"
26
+ class="addon"
27
+ /> -->
28
+
29
+ <!-- field -->
30
+ <!-- :options="field.options"
31
+ :disabled="field.disabled"
32
+ :maxlength="field.maxlength" -->
33
+ <AtomField
34
+ v-model="models[field.name]"
35
+ :type="field.type"
36
+ :field="field"
37
+ @blur.capture="
38
+ persist(field.name, models[field.name]);
39
+ routine({ models, field });
40
+ "
41
+ />
42
+ </MoleculeFieldGroup>
43
+ </div>
44
+ </OrganismForm>
45
+ </template>
46
+
47
+ <script setup lang="ts">
48
+ import { ref, computed } from "vue";
49
+ import { IMailing, IField, IRule, EFieldTypes } from "./types";
50
+ import OrganismForm from "./Organism.Form.vue";
51
+
52
+ interface IProps {
53
+ mailing: IMailing;
54
+ submit: Function;
55
+ deny?: Function;
56
+ persist?: Function;
57
+ routine?: Function;
58
+
59
+ defaults?: { [name: string]: string };
60
+ }
61
+ const props = defineProps<IProps>();
62
+
63
+ // computed
64
+ const getRulesByField = computed<Function>(
65
+ () =>
66
+ (value: string, { regex, required, messages, type }: IField): IRule => {
67
+ const isPhoneField = type === EFieldTypes.phone;
68
+ const isBrazilianPhone = /^\+55[0-9]/.test(value);
69
+ const errorMessage = messages?.error || "";
70
+
71
+ if (isPhoneField && isBrazilianPhone) {
72
+ regex = /^\+55[0-9]{10,11}$/;
73
+ }
74
+
75
+ return buildRule({ value, regex, required, errorMessage });
76
+ }
77
+ );
78
+
79
+ // data
80
+ const form = ref<InstanceType<typeof OrganismForm> | null>(null);
81
+
82
+ // methods
83
+ function buildRule({ value, regex, required, errorMessage }: any): IRule {
84
+ return () => {
85
+ if (value) return regex.test(value) || errorMessage;
86
+ if (required) return "Campo obrigatório!";
87
+
88
+ return true;
89
+ };
90
+ }
91
+
92
+ function persist(label: string, value: any) {
93
+ if (!props.persist) return;
94
+
95
+ props.persist({ [label]: value });
96
+ }
97
+
98
+ function routine({ models, field }: any) {
99
+ if (!props.routine) return;
100
+
101
+ props.routine({ models, field });
102
+ }
103
+
104
+ // expose
105
+ defineExpose({ form });
106
+ </script>
107
+
108
+ <style scoped>
109
+ .field-group {
110
+ position: relative;
111
+ }
112
+ </style>
@@ -0,0 +1,79 @@
1
+ <template>
2
+ <header
3
+ class="w-full h-16 fixed flex justify-between items-center text-white py-2 px-4 z-20 bg-slate-800"
4
+ >
5
+ <!-- info -->
6
+ <div class="flex items-center">
7
+ <!-- logo -->
8
+ <figure
9
+ class="h-full flex justify-center items-center border-r-2 mr-4 pr-3 border-slate-700"
10
+ >
11
+ <img
12
+ src="../public/192x192.png"
13
+ class="w-8 max-sm:hidden"
14
+ alt="ContactStudio Logo"
15
+ >
16
+ <i
17
+ v-if="navIsVisible"
18
+ class="bi-x-lg w-6 text-xl sm:hidden"
19
+ />
20
+ <i
21
+ v-if="!navIsVisible"
22
+ id="navopen"
23
+ class="bi-text-left w-6 text-2xl sm:hidden"
24
+ @click="showNav"
25
+ />
26
+ </figure>
27
+
28
+ <!-- user -->
29
+ <header>
30
+ <p
31
+ class="overflow-hidden text-ellipsis text-sm sm:max-w-[350px] max-w-[150px]"
32
+ v-text="user.nickname"
33
+ />
34
+
35
+ <p class="text-xs opacity-50">
36
+ {{ config.public.PROJECT }}
37
+ [{{ config.public.VERSION }}]
38
+ </p>
39
+ </header>
40
+ </div>
41
+
42
+ <!-- slots -->
43
+ <div class="flex">
44
+ <slot />
45
+ </div>
46
+ </header>
47
+ </template>
48
+
49
+ <script setup lang="ts">
50
+ import { ref } from "vue";
51
+ import { IUser } from "./types";
52
+ import { useRuntimeConfig, useNuxtApp } from "#app";
53
+
54
+ // props
55
+ interface IProps {
56
+ user: IUser;
57
+ }
58
+ defineProps<IProps>();
59
+
60
+ // app
61
+ const { $emit, $listen } = useNuxtApp();
62
+
63
+ // data
64
+ const config = useRuntimeConfig();
65
+ const navIsVisible = ref<boolean>(false);
66
+
67
+ // methods
68
+ function showNav(): void {
69
+ navIsVisible.value = true;
70
+ $emit("nav:show");
71
+ }
72
+
73
+ // emits
74
+ $listen("nav:hide", () => {
75
+ navIsVisible.value = false;
76
+ });
77
+ </script>
78
+
79
+ <style scoped></style>