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
package/README.md ADDED
@@ -0,0 +1,94 @@
1
+ <!--
2
+ Get your module up and running quickly.
3
+
4
+ Find and replace all on all files (CMD+SHIFT+F):
5
+ - Name: CS Tools
6
+ - Package name: @contactstudio/cstools
7
+ - Description: Nuxt Tools Module for ContactStudio
8
+ -->
9
+
10
+ # CS Tools
11
+
12
+ [![npm version][npm-version-src]][npm-version-href]
13
+ [![npm downloads][npm-downloads-src]][npm-downloads-href]
14
+ [![License][license-src]][license-href]
15
+ [![Nuxt][nuxt-src]][nuxt-href]
16
+
17
+ Nuxt Tools Module for ContactStudio for doing amazing things.
18
+
19
+ - [✨ &nbsp;Release Notes](/CHANGELOG.md)
20
+ <!-- - [🏀 Online playground](https://stackblitz.com/github/your-org/@contactstudio/cstools?file=playground%2Fapp.vue) -->
21
+ <!-- - [📖 &nbsp;Documentation](https://example.com) -->
22
+
23
+ ## Features
24
+
25
+ <!-- Highlight some of the features your module provide here -->
26
+
27
+ - ⛰ &nbsp;Foo
28
+ - 🚠 &nbsp;Bar
29
+ - 🌲 &nbsp;Baz
30
+
31
+ ## Quick Setup
32
+
33
+ 1. Add `@contactstudio/cstools` dependency to your project
34
+
35
+ ```bash
36
+ # Using pnpm
37
+ pnpm add -D @contactstudio/cstools
38
+
39
+ # Using yarn
40
+ yarn add --dev @contactstudio/cstools
41
+
42
+ # Using npm
43
+ npm install --save-dev @contactstudio/cstools
44
+ ```
45
+
46
+ 2. Add `@contactstudio/cstools` to the `modules` section of `nuxt.config.ts`
47
+
48
+ ```js
49
+ export default defineNuxtConfig({
50
+ modules: ["@contactstudio/cstools"],
51
+ });
52
+ ```
53
+
54
+ That's it! You can now use CS Tools in your Nuxt app ✨
55
+
56
+ ## Development
57
+
58
+ ```bash
59
+ # Watch generate CSS
60
+ npx tailwindcss -o ./src/runtime/index.css --minify --watch
61
+
62
+ # Install dependencies
63
+ npm install
64
+
65
+ # Generate type stubs
66
+ npm run dev:prepare
67
+
68
+ # Develop with the playground
69
+ npm run dev
70
+
71
+ # Build the playground
72
+ npm run dev:build
73
+
74
+ # Run ESLint
75
+ npm run lint
76
+
77
+ # Run Vitest
78
+ npm run test
79
+ npm run test:watch
80
+
81
+ # Release new version
82
+ npm run release
83
+ ```
84
+
85
+ <!-- Badges -->
86
+
87
+ [npm-version-src]: https://img.shields.io/npm/v/@contactstudio/cstools/latest.svg?style=flat&colorA=18181B&colorB=28CF8D
88
+ [npm-version-href]: https://npmjs.com/package/@contactstudio/cstools
89
+ [npm-downloads-src]: https://img.shields.io/npm/dm/@contactstudio/cstools.svg?style=flat&colorA=18181B&colorB=28CF8D
90
+ [npm-downloads-href]: https://npmjs.com/package/@contactstudio/cstools
91
+ [license-src]: https://img.shields.io/npm/l/@contactstudio/cstools.svg?style=flat&colorA=18181B&colorB=28CF8D
92
+ [license-href]: https://npmjs.com/package/@contactstudio/cstools
93
+ [nuxt-src]: https://img.shields.io/badge/Nuxt-18181B?logo=nuxt.js
94
+ [nuxt-href]: https://nuxt.com
@@ -0,0 +1,5 @@
1
+ module.exports = function(...args) {
2
+ return import('./module.mjs').then(m => m.default.call(this, ...args))
3
+ }
4
+ const _meta = module.exports.meta = require('./module.json')
5
+ module.exports.getMeta = () => Promise.resolve(_meta)
@@ -0,0 +1,7 @@
1
+ import * as _nuxt_schema from '@nuxt/schema';
2
+
3
+ interface ModuleOptions {
4
+ }
5
+ declare const _default: _nuxt_schema.NuxtModule<ModuleOptions>;
6
+
7
+ export { ModuleOptions, _default as default };
@@ -0,0 +1,5 @@
1
+ {
2
+ "name": "@contactstudio/cstools",
3
+ "configKey": "CSTools",
4
+ "version": "1.0.224"
5
+ }
@@ -0,0 +1,72 @@
1
+ import { defineNuxtModule, createResolver, addPlugin, addComponent } from '@nuxt/kit';
2
+ import { fileURLToPath } from 'url';
3
+ import { readdirSync } from 'fs';
4
+
5
+ const module = defineNuxtModule({
6
+ meta: {
7
+ name: "@contactstudio/cstools",
8
+ configKey: "CSTools"
9
+ },
10
+ // Default configuration options of the Nuxt module
11
+ defaults: {},
12
+ setup(options, nuxt) {
13
+ const { resolve } = createResolver(import.meta.url);
14
+ const runtimeDir = fileURLToPath(new URL("./runtime", import.meta.url));
15
+ const componentsDir = `${runtimeDir}/components`;
16
+ addPlugin(resolve("runtime/plugins/emitter"));
17
+ addPlugin(resolve("runtime/plugins/clickOutside"));
18
+ nuxt.options.css.push(`${runtimeDir}/index.css`);
19
+ setAliases(nuxt);
20
+ setCDNs(nuxt);
21
+ setLibPublic(nuxt);
22
+ const files = readdirSync(componentsDir);
23
+ for (const file of files) {
24
+ addComponent({
25
+ name: getFileName(file),
26
+ filePath: resolve(componentsDir, file)
27
+ });
28
+ }
29
+ }
30
+ });
31
+ function getFileName(file) {
32
+ const parts = file.split(".");
33
+ parts.pop();
34
+ return parts.join("");
35
+ }
36
+ function setCDNs(nuxt) {
37
+ const MINDBEUI_VERSION = nuxt.options.runtimeConfig.public.MINDBEUI_VERSION;
38
+ nuxt.options.app.head.link?.push({
39
+ rel: "stylesheet",
40
+ href: `https://cdn.jsdelivr.net/gh/lipis/flag-icons@6.11.0/css/flag-icons.min.css`
41
+ });
42
+ nuxt.options.app.head.link?.push({
43
+ rel: "stylesheet",
44
+ href: `https://unpkg.com/@contactstudio/mindbeui@${MINDBEUI_VERSION}/dist/mindbeui.css`
45
+ });
46
+ nuxt.options.app.head.script?.push({
47
+ src: `https://unpkg.com/@contactstudio/mindbeui@${MINDBEUI_VERSION}/dist/mindbeui.js`
48
+ });
49
+ }
50
+ function setAliases(nuxt) {
51
+ nuxt.options.alias = {
52
+ ...nuxt.options.alias,
53
+ types: fileURLToPath(
54
+ new URL("./runtime/components/types", import.meta.url)
55
+ ),
56
+ components: fileURLToPath(new URL("./runtime/components", import.meta.url)),
57
+ public: fileURLToPath(new URL("./runtime/public", import.meta.url))
58
+ };
59
+ }
60
+ function setLibPublic(nuxt) {
61
+ const { resolve } = createResolver(import.meta.url);
62
+ nuxt.hook("nitro:config", async (nitroConfig) => {
63
+ nitroConfig.publicAssets || (nitroConfig.publicAssets = []);
64
+ nitroConfig.publicAssets.push({
65
+ dir: resolve("./runtime/public"),
66
+ maxAge: 60 * 60 * 24 * 365
67
+ // 1 year
68
+ });
69
+ });
70
+ }
71
+
72
+ export { module as default };
@@ -0,0 +1,46 @@
1
+ <template>
2
+ <div
3
+ class="alert"
4
+ :class="[{ visible }, getType]"
5
+ >
6
+ {{ message }}
7
+ </div>
8
+ </template>
9
+
10
+ <script setup lang="ts">
11
+ import { ref, computed } from "vue";
12
+
13
+ // props
14
+ interface IProps {
15
+ timeout?: number
16
+ }
17
+ const props = defineProps<IProps>()
18
+
19
+ // computed
20
+ const getType = computed<string>(() => {
21
+ return `alert-${type.value}`
22
+ })
23
+
24
+ // data
25
+ const visible = ref<boolean>(false);
26
+ const type = ref<string>("")
27
+ const message = ref<string>("")
28
+
29
+ // methods
30
+ function show(tp: string, msg: string) {
31
+ if (visible.value) return;
32
+
33
+ type.value = tp
34
+ message.value = msg
35
+ visible.value = true;
36
+
37
+ setTimeout(() => {
38
+ visible.value = false;
39
+ }, props.timeout ?? 5000);
40
+ }
41
+
42
+ // expose
43
+ defineExpose({ show });
44
+ </script>
45
+
46
+ <style scoped></style>
@@ -0,0 +1,37 @@
1
+ <template>
2
+ <div />
3
+ </template>
4
+
5
+ <script setup lang="ts">
6
+ import { onMounted, onUpdated } from "vue"
7
+ import { useCookie, useNuxtApp } from "#app";
8
+
9
+ // props
10
+ interface IProps {
11
+ logout: Function
12
+ }
13
+ const props = defineProps<IProps>()
14
+
15
+ // hooks
16
+ onMounted(update)
17
+ onUpdated(update)
18
+
19
+ // emits
20
+ const { $listen } = useNuxtApp();
21
+ $listen("auth:logout", logout)
22
+ $listen("auth:update", update)
23
+
24
+ // methods
25
+ function update(): void {
26
+ const session = useCookie("cssession");
27
+
28
+ if (!session.value) {
29
+ props.logout()
30
+ }
31
+ }
32
+ function logout(): void {
33
+ props.logout()
34
+ }
35
+ </script>
36
+
37
+ <style scoped></style>
@@ -0,0 +1,18 @@
1
+ <template>
2
+ <div class="w-full flex flex-col justify-center items-center text-center p-10">
3
+ <img
4
+ src="../public/chat.svg"
5
+ class="max-w-md -mt-44 -mb-10 -ml-5"
6
+ >
7
+ <h4 class="text-lg mt-5 mb-1 text-primary font-medium">
8
+ Nenhuma conversa selecionada
9
+ </h4>
10
+ <p class="opacity-50">
11
+ Para iniciar o atendimento, selecione uma conversa do menu ao lado
12
+ </p>
13
+ </div>
14
+ </template>
15
+
16
+ <script setup lang="ts"></script>
17
+
18
+ <style scoped></style>
@@ -0,0 +1,28 @@
1
+ <template>
2
+ <div class="w-full h-screen relative flex flex-col md:flex-row overflow-hidden text-black dark:text-white">
3
+ <article class="flex-1 p-16">
4
+ <p class="font-bold mb-16 sm:mb-40">
5
+ 404 Not Found
6
+ </p>
7
+ <h1 class="text-8xl mb-8 font-bold">
8
+ Ohh não...
9
+ </h1>
10
+ <p class="text-lg opacity-50 mb-16">
11
+ Desculpe, infelizmente a página não pode ser encontrada.
12
+ </p>
13
+ <button
14
+ class="btn btn-outline-secondary text-base max-h-none px-6"
15
+ @click="$router.push('/')"
16
+ >
17
+ Página inicial
18
+ </button>
19
+ </article>
20
+ <aside class="flex">
21
+ <img src="../public/404.svg">
22
+ </aside>
23
+ </div>
24
+ </template>
25
+
26
+ <script setup lang="ts"></script>
27
+
28
+ <style scoped></style>
@@ -0,0 +1,18 @@
1
+ <template>
2
+ <article class="flex flex-col justify-center items-center text-center dark:text-white">
3
+ <h4 class="text-xl text-warn font-medium mb-1">
4
+ Área restrita.
5
+ </h4>
6
+ <p class="opacity-50 text-sm mb-10">
7
+ Você não possui permissão para acessar essa página
8
+ </p>
9
+ <img
10
+ src="../public/security.svg"
11
+ class="w-full max-w-sm -mt-10"
12
+ >
13
+ </article>
14
+ </template>
15
+
16
+ <script setup lang="ts"></script>
17
+
18
+ <style scoped></style>
@@ -0,0 +1,26 @@
1
+ <template>
2
+ <ul class="breadcrumbs">
3
+ <li
4
+ v-for="({ label, disabled, path }, i) in pages"
5
+ :key="i"
6
+ :disabled="disabled"
7
+ class="breadcrumb"
8
+ >
9
+ <NuxtLink :to="path">
10
+ {{ label }}
11
+ </NuxtLink>
12
+ </li>
13
+ </ul>
14
+ </template>
15
+
16
+ <script setup lang="ts">
17
+ import { IPages } from "./types";
18
+
19
+ // props
20
+ interface IProps {
21
+ pages: IPages;
22
+ }
23
+ defineProps<IProps>();
24
+ </script>
25
+
26
+ <style scoped></style>
@@ -0,0 +1,136 @@
1
+ <template>
2
+ <!-- contact -->
3
+ <li class="item flex items-center max-h-none !px-3 !rounded-none">
4
+ <!-- avatar -->
5
+ <figure
6
+ :class="[getStatusClass]"
7
+ :notifications="contact.notifications"
8
+ class="w-10 h-10 avatar avatar-soft-secondary"
9
+ >
10
+ <img
11
+ v-if="contact.avatar"
12
+ :src="contact.avatar"
13
+ class="rounded-md"
14
+ >
15
+ <i
16
+ v-else
17
+ class="bi-person-fill text-xl"
18
+ />
19
+ </figure>
20
+
21
+ <!-- info -->
22
+ <div class="flex-1 mx-3 w-24">
23
+ <p class="w-full overflow-hidden text-ellipsis whitespace-nowrap py-1">
24
+ <!-- label -->
25
+ <span class="text-xs">
26
+ {{ contact.label }}
27
+ </span>
28
+ <!-- hours -->
29
+ <span
30
+ v-if="entryDateExists"
31
+ class="badge badge-outline-secondary ml-2"
32
+ tooltip="Tempo em atendimento"
33
+ >
34
+ {{ new Date(milliseconds).toISOString().slice(11, 19) }}
35
+ </span>
36
+ </p>
37
+ <!-- typing -->
38
+ <p
39
+ v-if="contact.typing"
40
+ class="text-2xs text-success"
41
+ >
42
+ Digitando...
43
+ </p>
44
+ <!-- message -->
45
+ <div
46
+ v-else
47
+ class="markdown h-4 text-2xs opacity-50 w-full overflow-hidden text-ellipsis whitespace-nowrap"
48
+ v-html="marked(props.contact.message ?? '')"
49
+ />
50
+ </div>
51
+
52
+ <!-- date message -->
53
+ <p
54
+ v-if="!expirated"
55
+ class="badge !p-0 text-secondary"
56
+ tooltip="Horário da última mensagem"
57
+ >
58
+ {{ getDate }}
59
+ </p>
60
+
61
+ <!-- expirate -->
62
+ <div
63
+ v-if="expirated"
64
+ tooltip="Envie uma mensagem para o cliente!"
65
+ class="flex justify-center items-center relative mr-2"
66
+ >
67
+ <div class="w-5 h-5 rounded-full bg-error animate-ping absolute" />
68
+
69
+ <i class="bi bi-exclamation-circle-fill text-error text-base" />
70
+ </div>
71
+ </li>
72
+ </template>
73
+
74
+ <script setup lang="ts">
75
+ import { marked } from "marked";
76
+ import { ref, computed, onMounted } from "vue";
77
+ import { getMinutesBetweenDates, getTimeByDate, IChatContact } from "./types";
78
+
79
+ // props
80
+ interface IProps {
81
+ contact: IChatContact;
82
+ timeout: number;
83
+ }
84
+ const props = defineProps<IProps>();
85
+
86
+ // mounted
87
+ onMounted(() => {
88
+ setInterval(validateExpiration, 1000);
89
+ });
90
+
91
+ // computed
92
+ const getDate = computed<string>(() => {
93
+ const date = props.contact.date;
94
+ if (!date) return "";
95
+ return getTimeByDate(date);
96
+ });
97
+ const entryDateExists = computed<boolean>(() => !!props.contact.entrydate)
98
+ const getStatusClass = computed<string>(() => {
99
+ if (props.contact.on) return "avatar-status-success";
100
+ return "avatar-status-error";
101
+ });
102
+
103
+ // data
104
+ const expirated = ref<boolean>(false);
105
+ const milliseconds = ref<number>(0)
106
+
107
+ // methods
108
+ function validateExpiration() {
109
+ if (!props.contact.date) return;
110
+ expirated.value =
111
+ getMinutesBetweenDates(new Date(), props.contact.date) >= props.timeout;
112
+ }
113
+ async function setTimerContact(): Promise<void> {
114
+ if (!props.contact.entrydate) return
115
+
116
+ await new Promise(resolve => setTimeout(resolve, 1000))
117
+
118
+ const now = new Date().getTime()
119
+ const entry = props.contact.entrydate.getTime()
120
+
121
+ milliseconds.value = Math.abs(now - entry)
122
+
123
+ setTimerContact()
124
+ }
125
+ setTimerContact()
126
+ </script>
127
+
128
+ <style scoped>
129
+ .min-w-40px {
130
+ min-width: 40px;
131
+ }
132
+
133
+ .text-2xs {
134
+ font-size: 0.7rem;
135
+ }
136
+ </style>
@@ -0,0 +1,87 @@
1
+ <template>
2
+ <!-- schedule -->
3
+ <li class="flex flex-wrap gap-2 items-center max-h-none !px-3 !rounded-none">
4
+ <!-- avatar -->
5
+ <figure class="w-10 h-10 avatar avatar-soft-schedule [&::after]:!hidden">
6
+ <img
7
+ v-if="schedule.avatar"
8
+ :src="schedule.avatar"
9
+ class="rounded-md"
10
+ >
11
+ <i
12
+ v-else
13
+ class="bi bi-chat-dots-fill text-xl"
14
+ />
15
+ </figure>
16
+
17
+ <!-- info -->
18
+ <div class="flex-1 w-24">
19
+ <p class="w-full overflow-hidden text-ellipsis whitespace-nowrap">
20
+ <span class="text-xs">
21
+ {{ schedule.identifier }}
22
+ </span>
23
+ </p>
24
+ <p class="markdown h-4 text-2xs opacity-50 w-full overflow-hidden text-ellipsis whitespace-nowrap">
25
+ {{ schedule.date.toLocaleString() }}
26
+ </p>
27
+ </div>
28
+
29
+ <div
30
+ v-if="slotsExists"
31
+ class="w-full accordion accordion-bordered"
32
+ >
33
+ <details class="item">
34
+ <summary class="text-xs">
35
+ Mais Detalhes
36
+ </summary>
37
+ <article>
38
+ <table class="w-full">
39
+ <tr
40
+ v-for="(value, field) in schedule.slots"
41
+ v-show="value"
42
+ :key="field"
43
+ >
44
+ <td class="pt-2 text-xs">
45
+ {{ field }}
46
+ </td>
47
+ <td
48
+ class="pt-2"
49
+ align="right"
50
+ >
51
+ <span class="badge badge-soft-secondary">{{ value }}</span>
52
+ </td>
53
+ </tr>
54
+ </table>
55
+ </article>
56
+ </details>
57
+ </div>
58
+ </li>
59
+ </template>
60
+
61
+ <script setup lang="ts">
62
+ import { computed } from "vue";
63
+ import { IPortfolioSchedule } from "./types";
64
+
65
+ // props
66
+ interface IProps {
67
+ schedule: IPortfolioSchedule;
68
+ }
69
+ const props = defineProps<IProps>();
70
+
71
+ // computed
72
+ const slotsExists = computed<boolean>(() => {
73
+ if (!props.schedule.slots) return false;
74
+ return !!Object.entries(props.schedule.slots).length;
75
+ });
76
+ </script>
77
+
78
+ <style scoped>
79
+ .avatar-soft-schedule {
80
+ background-color: #f1f5f9;
81
+ color: #7E22CE;
82
+ }
83
+ .dark .avatar-soft-schedule {
84
+ background-color: #1e293b!important;
85
+ color: #A855F7;
86
+ }
87
+ </style>
@@ -0,0 +1,25 @@
1
+ <template>
2
+ <div class="flex justify-between opacity-50">
3
+ <span class="text-2xs mr-10">
4
+ {{ message.who }}
5
+ </span>
6
+ <span class="text-2xs">
7
+ {{ getTimeByDate(message.date) }}
8
+ </span>
9
+ </div>
10
+ </template>
11
+
12
+ <script setup lang="ts">
13
+ import { getTimeByDate, IChatMessage } from "./types";
14
+ // props
15
+ interface IProps {
16
+ message: IChatMessage;
17
+ }
18
+ defineProps<IProps>();
19
+ </script>
20
+
21
+ <style scoped>
22
+ .text-2xs {
23
+ font-size: 0.7rem;
24
+ }
25
+ </style>
@@ -0,0 +1,67 @@
1
+ <template>
2
+ <label
3
+ class="flex items-center h-5 w-10 rounded-full cursor-pointer border bg-gray-200 border-gray-400 dark:border-gray-600 dark:bg-gray-700"
4
+ @click="change"
5
+ >
6
+ <figure
7
+ :class="{ 'translate-x-full': dark }"
8
+ class="rounded-full w-1/2 h-full flex justify-center items-center select-none transition-transform bg-white dark:bg-gray-900"
9
+ >
10
+ <!-- darkmode -->
11
+ <i
12
+ v-if="dark"
13
+ class="bi bi-moon-fill leading-none"
14
+ />
15
+
16
+ <!-- lightmode -->
17
+ <i
18
+ v-else
19
+ class="bi bi-sun leading-none"
20
+ />
21
+ </figure>
22
+ </label>
23
+ </template>
24
+
25
+ <script setup lang="ts">
26
+ import { useNuxtApp, useCookie } from "#app";
27
+
28
+ // app
29
+ const { $listen } = useNuxtApp();
30
+
31
+ // data
32
+ const dark = useCookie<boolean>("darkmode", {
33
+ default: () => false,
34
+ });
35
+
36
+ // methods
37
+ function change(): void {
38
+ dark.value = !dark.value;
39
+
40
+ update();
41
+ }
42
+ function update(): void {
43
+ if (!process.client) return;
44
+
45
+ if (dark.value) {
46
+ document.body.classList.add("dark");
47
+ return;
48
+ }
49
+
50
+ document.body.classList.remove("dark");
51
+ }
52
+
53
+ update();
54
+
55
+ // emits
56
+ $listen("darkmode:change", change);
57
+ </script>
58
+
59
+ <style scoped>
60
+ .right {
61
+ transform: translateX(100%);
62
+ }
63
+
64
+ i {
65
+ font-size: 9px;
66
+ }
67
+ </style>