init frontend
12
frontend/.editorconfig
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
end_of_line = lf
|
||||||
|
insert_final_newline = true
|
||||||
|
|
||||||
|
# Matches multiple files with brace expansion notation
|
||||||
|
[*.{js,jsx,html,sass,vue,ts,tsx,json}]
|
||||||
|
charset = utf-8
|
||||||
|
indent_style = tab
|
||||||
|
indent_size = 4
|
||||||
|
trim_trailing_whitespace = true
|
10
frontend/.gitignore
vendored
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
node_modules
|
||||||
|
.DS_Store
|
||||||
|
dist
|
||||||
|
dist-ssr
|
||||||
|
*.local
|
||||||
|
.*-debug.log
|
||||||
|
*.log
|
||||||
|
.vercel
|
||||||
|
.vite-ssg-temp
|
||||||
|
.idea
|
9
frontend/.prettierrc
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
"arrowParens": "avoid",
|
||||||
|
"semi": true,
|
||||||
|
"tabWidth": 4,
|
||||||
|
"useTabs": true,
|
||||||
|
"vueIndentScriptAndStyle": true,
|
||||||
|
"singleQuote": true,
|
||||||
|
"trailingComma": "es5"
|
||||||
|
}
|
21
frontend/LICENSE
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2021 Christopher Reeve
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
137
frontend/README.md
Normal file
|
@ -0,0 +1,137 @@
|
||||||
|
<p align='center'>
|
||||||
|
<img src='https://user-images.githubusercontent.com/45350572/138070856-731c849a-466b-41a2-b39d-c5b5e76e94fa.png' alt='Vitailse - Opinionated Vite Starter Template with TailwindCSS' width='300'/>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
Opinionated Vite starter template with [TailwindCSS](https://tailwindcss.com/)
|
||||||
|
|
||||||
|
Inspired by [Vitesse](https://github.com/antfu/vitesse) ❤
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- ⚡️ [Vue 3](https://github.com/vuejs/vue-next), [Vite 2](https://github.com/vitejs/vite), [pnpm](https://pnpm.js.org/), [ESBuild](https://github.com/evanw/esbuild) - born with fastness
|
||||||
|
|
||||||
|
- 🗂 [File based routing](./src/pages)
|
||||||
|
|
||||||
|
- 📦 [Components auto importing](./src/components)
|
||||||
|
|
||||||
|
- 🍍 [State Management via Pinia](https://pinia.esm.dev/)
|
||||||
|
|
||||||
|
- 📑 [Layout system](./src/layouts)
|
||||||
|
|
||||||
|
- 📲 [PWA](https://github.com/antfu/vite-plugin-pwa)
|
||||||
|
|
||||||
|
- 🌍 [I18n ready](./locales)
|
||||||
|
|
||||||
|
- 🎨 [Tailwind CSS](https://tailwindcss.com/) - Rapidly build modern websites without ever leaving your HTML.
|
||||||
|
|
||||||
|
- 😃 [Use icons from any icon sets, with no compromise](https://github.com/antfu/unplugin-icons)
|
||||||
|
|
||||||
|
- 🔥 Use the [new `<script setup>` syntax](https://github.com/vuejs/rfcs/pull/227)
|
||||||
|
|
||||||
|
- 📥 [APIs auto importing](https://github.com/antfu/unplugin-auto-import) - use Composition API and others directly
|
||||||
|
|
||||||
|
- 🖨 Server-side generation (SSG) via [vite-ssg](https://github.com/antfu/vite-ssg)
|
||||||
|
|
||||||
|
- 🦔 Critical CSS via [critters](https://github.com/GoogleChromeLabs/critters)
|
||||||
|
|
||||||
|
- 🦾 TypeScript, of course
|
||||||
|
|
||||||
|
## Pre-packed
|
||||||
|
|
||||||
|
### UI Frameworks
|
||||||
|
|
||||||
|
- [TailwindCSS](https://tailwindcss.com/)
|
||||||
|
- [TailwindCSS Typography](https://github.com/tailwindlabs/tailwindcss-typography)
|
||||||
|
- [TailwindCSS Forms](https://github.com/tailwindlabs/tailwindcss-forms)
|
||||||
|
- [TailwindCSS Aspect Ratio](https://github.com/tailwindlabs/tailwindcss-aspect-ratio)
|
||||||
|
|
||||||
|
### Icons
|
||||||
|
|
||||||
|
- [Iconify](https://iconify.design) - use icons from any icon sets
|
||||||
|
- [`unplugin-icons`](https://github.com/antfu/unplugin-icons) - icons as Vue components
|
||||||
|
|
||||||
|
### Plugins
|
||||||
|
|
||||||
|
- [Vue Router](https://github.com/vuejs/vue-router)
|
||||||
|
- [`vite-plugin-pages`](https://github.com/hannoeru/vite-plugin-pages) - file system based routing
|
||||||
|
- [`vite-plugin-vue-layouts`](https://github.com/JohnCampionJr/vite-plugin-vue-layouts) - layouts for pages
|
||||||
|
- [Pinia](https://pinia.esm.dev) - Intuitive, type safe, light and flexible Store for Vue using the composition api
|
||||||
|
- [`unplugin-vue-components`](https://github.com/antfu/unplugin-vue-components) - components auto import
|
||||||
|
- [`unplugin-auto-import`](https://github.com/antfu/unplugin-auto-import) - Directly use Vue Composition API and others without importing
|
||||||
|
- [VueUse](https://github.com/antfu/vueuse) - collection of useful composition APIs
|
||||||
|
- [`@vueuse/head`](https://github.com/vueuse/head) - manipulate document head reactively
|
||||||
|
- [Vue I18n](https://github.com/intlify/vue-i18n-next) - Internationalization
|
||||||
|
- [`vite-plugin-vue-i18n`](https://github.com/intlify/vite-plugin-vue-i18n) - Vite plugin for Vue I18n
|
||||||
|
- [`vite-plugin-pwa`](https://github.com/antfu/vite-plugin-pwa) - PWA
|
||||||
|
|
||||||
|
### Coding Style
|
||||||
|
|
||||||
|
- Use Composition API with [`<script setup>` SFC syntax](https://github.com/vuejs/rfcs/pull/227)
|
||||||
|
|
||||||
|
### Dev tools
|
||||||
|
|
||||||
|
- [TypeScript](https://www.typescriptlang.org/)
|
||||||
|
- [pnpm](https://pnpm.js.org/) - fast, disk space efficient package manager
|
||||||
|
- [`vite-ssg`](https://github.com/antfu/vite-ssg) - Server-side generation
|
||||||
|
- [critters](https://github.com/GoogleChromeLabs/critters) - Critical CSS
|
||||||
|
- [VS Code Extensions](./.vscode/extensions.json)
|
||||||
|
- [Vite](https://marketplace.visualstudio.com/items?itemName=antfu.vite) - Fire up Vite server automatically
|
||||||
|
- [Volar](https://marketplace.visualstudio.com/items?itemName=johnsoncodehk.volar) - Vue 3 `<script setup>` IDE support
|
||||||
|
- [Iconify IntelliSense](https://marketplace.visualstudio.com/items?itemName=antfu.iconify) - Icon inline display and autocomplete
|
||||||
|
- [TailwindCSS Intellisense](https://marketplace.visualstudio.com/items?itemName=bradlc.vscode-tailwindcss) - IDE support for Tailwind CSS
|
||||||
|
- [i18n Ally](https://marketplace.visualstudio.com/items?itemName=lokalise.i18n-ally) - All in one i18n support
|
||||||
|
## Try it now!
|
||||||
|
### GitHub Template
|
||||||
|
|
||||||
|
[Create a repo from this template on GitHub](https://github.com/zynth17/vitailse/generate).
|
||||||
|
|
||||||
|
### Clone to local
|
||||||
|
|
||||||
|
If you prefer to do it manually with the cleaner git history
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npx degit zynth17/vitailse my-vitailse-app
|
||||||
|
cd my-vitailse-app
|
||||||
|
pnpm i # If you don't have pnpm installed, run: npm install -g pnpm
|
||||||
|
```
|
||||||
|
|
||||||
|
## Checklist
|
||||||
|
|
||||||
|
When you use this template, try follow the checklist to update your info properly
|
||||||
|
|
||||||
|
- [ ] Rename `name` field in `package.json`
|
||||||
|
- [ ] Change the author name in `LICENSE`
|
||||||
|
- [ ] Change the title in `App.vue`
|
||||||
|
- [ ] Change the favicon in `public`
|
||||||
|
- [ ] Remove the `.github` folder which contains the funding info
|
||||||
|
- [ ] Clean up the READMEs and remove routes
|
||||||
|
|
||||||
|
And, enjoy :)
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
### Development
|
||||||
|
|
||||||
|
Just run and visit http://localhost:3000
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pnpm dev
|
||||||
|
```
|
||||||
|
|
||||||
|
### Preview in Https
|
||||||
|
|
||||||
|
Just run and visit https://localhost
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pnpm build && pnpm run https-preview
|
||||||
|
```
|
||||||
|
|
||||||
|
### Build
|
||||||
|
|
||||||
|
To build the App, run
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pnpm build
|
||||||
|
```
|
||||||
|
|
||||||
|
And you will see the generated file in `dist` that ready to be served.
|
252
frontend/auto-imports.d.ts
vendored
Normal file
|
@ -0,0 +1,252 @@
|
||||||
|
// Generated by 'unplugin-auto-import'
|
||||||
|
export {}
|
||||||
|
declare global {
|
||||||
|
const EffectScope: typeof import('vue')['EffectScope']
|
||||||
|
const asyncComputed: typeof import('@vueuse/core')['asyncComputed']
|
||||||
|
const autoResetRef: typeof import('@vueuse/core')['autoResetRef']
|
||||||
|
const computed: typeof import('vue')['computed']
|
||||||
|
const computedAsync: typeof import('@vueuse/core')['computedAsync']
|
||||||
|
const computedEager: typeof import('@vueuse/core')['computedEager']
|
||||||
|
const computedInject: typeof import('@vueuse/core')['computedInject']
|
||||||
|
const computedWithControl: typeof import('@vueuse/core')['computedWithControl']
|
||||||
|
const controlledComputed: typeof import('@vueuse/core')['controlledComputed']
|
||||||
|
const controlledRef: typeof import('@vueuse/core')['controlledRef']
|
||||||
|
const createApp: typeof import('vue')['createApp']
|
||||||
|
const createEventHook: typeof import('@vueuse/core')['createEventHook']
|
||||||
|
const createGlobalState: typeof import('@vueuse/core')['createGlobalState']
|
||||||
|
const createInjectionState: typeof import('@vueuse/core')['createInjectionState']
|
||||||
|
const createReactiveFn: typeof import('@vueuse/core')['createReactiveFn']
|
||||||
|
const createSharedComposable: typeof import('@vueuse/core')['createSharedComposable']
|
||||||
|
const createUnrefFn: typeof import('@vueuse/core')['createUnrefFn']
|
||||||
|
const customRef: typeof import('vue')['customRef']
|
||||||
|
const debouncedRef: typeof import('@vueuse/core')['debouncedRef']
|
||||||
|
const debouncedWatch: typeof import('@vueuse/core')['debouncedWatch']
|
||||||
|
const defineAsyncComponent: typeof import('vue')['defineAsyncComponent']
|
||||||
|
const defineComponent: typeof import('vue')['defineComponent']
|
||||||
|
const eagerComputed: typeof import('@vueuse/core')['eagerComputed']
|
||||||
|
const effectScope: typeof import('vue')['effectScope']
|
||||||
|
const extendRef: typeof import('@vueuse/core')['extendRef']
|
||||||
|
const getCurrentInstance: typeof import('vue')['getCurrentInstance']
|
||||||
|
const getCurrentScope: typeof import('vue')['getCurrentScope']
|
||||||
|
const h: typeof import('vue')['h']
|
||||||
|
const ignorableWatch: typeof import('@vueuse/core')['ignorableWatch']
|
||||||
|
const inject: typeof import('vue')['inject']
|
||||||
|
const isDefined: typeof import('@vueuse/core')['isDefined']
|
||||||
|
const isProxy: typeof import('vue')['isProxy']
|
||||||
|
const isReactive: typeof import('vue')['isReactive']
|
||||||
|
const isReadonly: typeof import('vue')['isReadonly']
|
||||||
|
const isRef: typeof import('vue')['isRef']
|
||||||
|
const logicAnd: typeof import('@vueuse/core')['logicAnd']
|
||||||
|
const logicNot: typeof import('@vueuse/core')['logicNot']
|
||||||
|
const logicOr: typeof import('@vueuse/core')['logicOr']
|
||||||
|
const makeDestructurable: typeof import('@vueuse/core')['makeDestructurable']
|
||||||
|
const markRaw: typeof import('vue')['markRaw']
|
||||||
|
const nextTick: typeof import('vue')['nextTick']
|
||||||
|
const onActivated: typeof import('vue')['onActivated']
|
||||||
|
const onBeforeMount: typeof import('vue')['onBeforeMount']
|
||||||
|
const onBeforeUnmount: typeof import('vue')['onBeforeUnmount']
|
||||||
|
const onBeforeUpdate: typeof import('vue')['onBeforeUpdate']
|
||||||
|
const onClickOutside: typeof import('@vueuse/core')['onClickOutside']
|
||||||
|
const onDeactivated: typeof import('vue')['onDeactivated']
|
||||||
|
const onErrorCaptured: typeof import('vue')['onErrorCaptured']
|
||||||
|
const onKeyStroke: typeof import('@vueuse/core')['onKeyStroke']
|
||||||
|
const onLongPress: typeof import('@vueuse/core')['onLongPress']
|
||||||
|
const onMounted: typeof import('vue')['onMounted']
|
||||||
|
const onRenderTracked: typeof import('vue')['onRenderTracked']
|
||||||
|
const onRenderTriggered: typeof import('vue')['onRenderTriggered']
|
||||||
|
const onScopeDispose: typeof import('vue')['onScopeDispose']
|
||||||
|
const onServerPrefetch: typeof import('vue')['onServerPrefetch']
|
||||||
|
const onStartTyping: typeof import('@vueuse/core')['onStartTyping']
|
||||||
|
const onUnmounted: typeof import('vue')['onUnmounted']
|
||||||
|
const onUpdated: typeof import('vue')['onUpdated']
|
||||||
|
const pausableWatch: typeof import('@vueuse/core')['pausableWatch']
|
||||||
|
const provide: typeof import('vue')['provide']
|
||||||
|
const reactify: typeof import('@vueuse/core')['reactify']
|
||||||
|
const reactifyObject: typeof import('@vueuse/core')['reactifyObject']
|
||||||
|
const reactive: typeof import('vue')['reactive']
|
||||||
|
const reactiveComputed: typeof import('@vueuse/core')['reactiveComputed']
|
||||||
|
const reactiveOmit: typeof import('@vueuse/core')['reactiveOmit']
|
||||||
|
const reactivePick: typeof import('@vueuse/core')['reactivePick']
|
||||||
|
const readonly: typeof import('vue')['readonly']
|
||||||
|
const ref: typeof import('vue')['ref']
|
||||||
|
const refAutoReset: typeof import('@vueuse/core')['refAutoReset']
|
||||||
|
const refDebounced: typeof import('@vueuse/core')['refDebounced']
|
||||||
|
const refDefault: typeof import('@vueuse/core')['refDefault']
|
||||||
|
const refThrottled: typeof import('@vueuse/core')['refThrottled']
|
||||||
|
const refWithControl: typeof import('@vueuse/core')['refWithControl']
|
||||||
|
const resolveComponent: typeof import('vue')['resolveComponent']
|
||||||
|
const resolveRef: typeof import('@vueuse/core')['resolveRef']
|
||||||
|
const resolveUnref: typeof import('@vueuse/core')['resolveUnref']
|
||||||
|
const shallowReactive: typeof import('vue')['shallowReactive']
|
||||||
|
const shallowReadonly: typeof import('vue')['shallowReadonly']
|
||||||
|
const shallowRef: typeof import('vue')['shallowRef']
|
||||||
|
const syncRef: typeof import('@vueuse/core')['syncRef']
|
||||||
|
const syncRefs: typeof import('@vueuse/core')['syncRefs']
|
||||||
|
const templateRef: typeof import('@vueuse/core')['templateRef']
|
||||||
|
const throttledRef: typeof import('@vueuse/core')['throttledRef']
|
||||||
|
const throttledWatch: typeof import('@vueuse/core')['throttledWatch']
|
||||||
|
const toRaw: typeof import('vue')['toRaw']
|
||||||
|
const toReactive: typeof import('@vueuse/core')['toReactive']
|
||||||
|
const toRef: typeof import('vue')['toRef']
|
||||||
|
const toRefs: typeof import('vue')['toRefs']
|
||||||
|
const triggerRef: typeof import('vue')['triggerRef']
|
||||||
|
const tryOnBeforeMount: typeof import('@vueuse/core')['tryOnBeforeMount']
|
||||||
|
const tryOnBeforeUnmount: typeof import('@vueuse/core')['tryOnBeforeUnmount']
|
||||||
|
const tryOnMounted: typeof import('@vueuse/core')['tryOnMounted']
|
||||||
|
const tryOnScopeDispose: typeof import('@vueuse/core')['tryOnScopeDispose']
|
||||||
|
const tryOnUnmounted: typeof import('@vueuse/core')['tryOnUnmounted']
|
||||||
|
const unref: typeof import('vue')['unref']
|
||||||
|
const unrefElement: typeof import('@vueuse/core')['unrefElement']
|
||||||
|
const until: typeof import('@vueuse/core')['until']
|
||||||
|
const useActiveElement: typeof import('@vueuse/core')['useActiveElement']
|
||||||
|
const useAsyncQueue: typeof import('@vueuse/core')['useAsyncQueue']
|
||||||
|
const useAsyncState: typeof import('@vueuse/core')['useAsyncState']
|
||||||
|
const useAttrs: typeof import('vue')['useAttrs']
|
||||||
|
const useBase64: typeof import('@vueuse/core')['useBase64']
|
||||||
|
const useBattery: typeof import('@vueuse/core')['useBattery']
|
||||||
|
const useBluetooth: typeof import('@vueuse/core')['useBluetooth']
|
||||||
|
const useBreakpoints: typeof import('@vueuse/core')['useBreakpoints']
|
||||||
|
const useBroadcastChannel: typeof import('@vueuse/core')['useBroadcastChannel']
|
||||||
|
const useBrowserLocation: typeof import('@vueuse/core')['useBrowserLocation']
|
||||||
|
const useCached: typeof import('@vueuse/core')['useCached']
|
||||||
|
const useClamp: typeof import('@vueuse/core')['useClamp']
|
||||||
|
const useClipboard: typeof import('@vueuse/core')['useClipboard']
|
||||||
|
const useColorMode: typeof import('@vueuse/core')['useColorMode']
|
||||||
|
const useConfirmDialog: typeof import('@vueuse/core')['useConfirmDialog']
|
||||||
|
const useCounter: typeof import('@vueuse/core')['useCounter']
|
||||||
|
const useCssModule: typeof import('vue')['useCssModule']
|
||||||
|
const useCssVar: typeof import('@vueuse/core')['useCssVar']
|
||||||
|
const useCssVars: typeof import('vue')['useCssVars']
|
||||||
|
const useCurrentElement: typeof import('@vueuse/core')['useCurrentElement']
|
||||||
|
const useCycleList: typeof import('@vueuse/core')['useCycleList']
|
||||||
|
const useDark: typeof import('@vueuse/core')['useDark']
|
||||||
|
const useDateFormat: typeof import('@vueuse/core')['useDateFormat']
|
||||||
|
const useDebounce: typeof import('@vueuse/core')['useDebounce']
|
||||||
|
const useDebounceFn: typeof import('@vueuse/core')['useDebounceFn']
|
||||||
|
const useDebouncedRefHistory: typeof import('@vueuse/core')['useDebouncedRefHistory']
|
||||||
|
const useDeviceMotion: typeof import('@vueuse/core')['useDeviceMotion']
|
||||||
|
const useDeviceOrientation: typeof import('@vueuse/core')['useDeviceOrientation']
|
||||||
|
const useDevicePixelRatio: typeof import('@vueuse/core')['useDevicePixelRatio']
|
||||||
|
const useDevicesList: typeof import('@vueuse/core')['useDevicesList']
|
||||||
|
const useDisplayMedia: typeof import('@vueuse/core')['useDisplayMedia']
|
||||||
|
const useDocumentVisibility: typeof import('@vueuse/core')['useDocumentVisibility']
|
||||||
|
const useDraggable: typeof import('@vueuse/core')['useDraggable']
|
||||||
|
const useDropZone: typeof import('@vueuse/core')['useDropZone']
|
||||||
|
const useElementBounding: typeof import('@vueuse/core')['useElementBounding']
|
||||||
|
const useElementByPoint: typeof import('@vueuse/core')['useElementByPoint']
|
||||||
|
const useElementHover: typeof import('@vueuse/core')['useElementHover']
|
||||||
|
const useElementSize: typeof import('@vueuse/core')['useElementSize']
|
||||||
|
const useElementVisibility: typeof import('@vueuse/core')['useElementVisibility']
|
||||||
|
const useEventBus: typeof import('@vueuse/core')['useEventBus']
|
||||||
|
const useEventListener: typeof import('@vueuse/core')['useEventListener']
|
||||||
|
const useEventSource: typeof import('@vueuse/core')['useEventSource']
|
||||||
|
const useEyeDropper: typeof import('@vueuse/core')['useEyeDropper']
|
||||||
|
const useFavicon: typeof import('@vueuse/core')['useFavicon']
|
||||||
|
const useFetch: typeof import('@vueuse/core')['useFetch']
|
||||||
|
const useFileDialog: typeof import('@vueuse/core')['useFileDialog']
|
||||||
|
const useFileSystemAccess: typeof import('@vueuse/core')['useFileSystemAccess']
|
||||||
|
const useFocus: typeof import('@vueuse/core')['useFocus']
|
||||||
|
const useFocusWithin: typeof import('@vueuse/core')['useFocusWithin']
|
||||||
|
const useFps: typeof import('@vueuse/core')['useFps']
|
||||||
|
const useFullscreen: typeof import('@vueuse/core')['useFullscreen']
|
||||||
|
const useGamepad: typeof import('@vueuse/core')['useGamepad']
|
||||||
|
const useGeolocation: typeof import('@vueuse/core')['useGeolocation']
|
||||||
|
const useHead: typeof import('@vueuse/head')['useHead']
|
||||||
|
const useI18n: typeof import('vue-i18n')['useI18n']
|
||||||
|
const useIdle: typeof import('@vueuse/core')['useIdle']
|
||||||
|
const useImage: typeof import('@vueuse/core')['useImage']
|
||||||
|
const useInfiniteScroll: typeof import('@vueuse/core')['useInfiniteScroll']
|
||||||
|
const useIntersectionObserver: typeof import('@vueuse/core')['useIntersectionObserver']
|
||||||
|
const useInterval: typeof import('@vueuse/core')['useInterval']
|
||||||
|
const useIntervalFn: typeof import('@vueuse/core')['useIntervalFn']
|
||||||
|
const useKeyModifier: typeof import('@vueuse/core')['useKeyModifier']
|
||||||
|
const useLastChanged: typeof import('@vueuse/core')['useLastChanged']
|
||||||
|
const useLocalStorage: typeof import('@vueuse/core')['useLocalStorage']
|
||||||
|
const useMagicKeys: typeof import('@vueuse/core')['useMagicKeys']
|
||||||
|
const useManualRefHistory: typeof import('@vueuse/core')['useManualRefHistory']
|
||||||
|
const useMediaControls: typeof import('@vueuse/core')['useMediaControls']
|
||||||
|
const useMediaQuery: typeof import('@vueuse/core')['useMediaQuery']
|
||||||
|
const useMemoize: typeof import('@vueuse/core')['useMemoize']
|
||||||
|
const useMemory: typeof import('@vueuse/core')['useMemory']
|
||||||
|
const useMounted: typeof import('@vueuse/core')['useMounted']
|
||||||
|
const useMouse: typeof import('@vueuse/core')['useMouse']
|
||||||
|
const useMouseInElement: typeof import('@vueuse/core')['useMouseInElement']
|
||||||
|
const useMousePressed: typeof import('@vueuse/core')['useMousePressed']
|
||||||
|
const useMutationObserver: typeof import('@vueuse/core')['useMutationObserver']
|
||||||
|
const useNavigatorLanguage: typeof import('@vueuse/core')['useNavigatorLanguage']
|
||||||
|
const useNetwork: typeof import('@vueuse/core')['useNetwork']
|
||||||
|
const useNow: typeof import('@vueuse/core')['useNow']
|
||||||
|
const useObjectUrl: typeof import('@vueuse/core')['useObjectUrl']
|
||||||
|
const useOffsetPagination: typeof import('@vueuse/core')['useOffsetPagination']
|
||||||
|
const useOnline: typeof import('@vueuse/core')['useOnline']
|
||||||
|
const usePageLeave: typeof import('@vueuse/core')['usePageLeave']
|
||||||
|
const useParallax: typeof import('@vueuse/core')['useParallax']
|
||||||
|
const usePermission: typeof import('@vueuse/core')['usePermission']
|
||||||
|
const usePointer: typeof import('@vueuse/core')['usePointer']
|
||||||
|
const usePointerSwipe: typeof import('@vueuse/core')['usePointerSwipe']
|
||||||
|
const usePreferredColorScheme: typeof import('@vueuse/core')['usePreferredColorScheme']
|
||||||
|
const usePreferredDark: typeof import('@vueuse/core')['usePreferredDark']
|
||||||
|
const usePreferredLanguages: typeof import('@vueuse/core')['usePreferredLanguages']
|
||||||
|
const useRafFn: typeof import('@vueuse/core')['useRafFn']
|
||||||
|
const useRefHistory: typeof import('@vueuse/core')['useRefHistory']
|
||||||
|
const useResizeObserver: typeof import('@vueuse/core')['useResizeObserver']
|
||||||
|
const useRoute: typeof import('@vue-router')['useRoute']
|
||||||
|
const useRouter: typeof import('@vue-router')['useRouter']
|
||||||
|
const useScreenOrientation: typeof import('@vueuse/core')['useScreenOrientation']
|
||||||
|
const useScreenSafeArea: typeof import('@vueuse/core')['useScreenSafeArea']
|
||||||
|
const useScriptTag: typeof import('@vueuse/core')['useScriptTag']
|
||||||
|
const useScroll: typeof import('@vueuse/core')['useScroll']
|
||||||
|
const useScrollLock: typeof import('@vueuse/core')['useScrollLock']
|
||||||
|
const useSessionStorage: typeof import('@vueuse/core')['useSessionStorage']
|
||||||
|
const useShare: typeof import('@vueuse/core')['useShare']
|
||||||
|
const useSlots: typeof import('vue')['useSlots']
|
||||||
|
const useSpeechRecognition: typeof import('@vueuse/core')['useSpeechRecognition']
|
||||||
|
const useSpeechSynthesis: typeof import('@vueuse/core')['useSpeechSynthesis']
|
||||||
|
const useStepper: typeof import('@vueuse/core')['useStepper']
|
||||||
|
const useStorage: typeof import('@vueuse/core')['useStorage']
|
||||||
|
const useStorageAsync: typeof import('@vueuse/core')['useStorageAsync']
|
||||||
|
const useStyleTag: typeof import('@vueuse/core')['useStyleTag']
|
||||||
|
const useSwipe: typeof import('@vueuse/core')['useSwipe']
|
||||||
|
const useTemplateRefsList: typeof import('@vueuse/core')['useTemplateRefsList']
|
||||||
|
const useTextSelection: typeof import('@vueuse/core')['useTextSelection']
|
||||||
|
const useTextareaAutosize: typeof import('@vueuse/core')['useTextareaAutosize']
|
||||||
|
const useThrottle: typeof import('@vueuse/core')['useThrottle']
|
||||||
|
const useThrottleFn: typeof import('@vueuse/core')['useThrottleFn']
|
||||||
|
const useThrottledRefHistory: typeof import('@vueuse/core')['useThrottledRefHistory']
|
||||||
|
const useTimeAgo: typeof import('@vueuse/core')['useTimeAgo']
|
||||||
|
const useTimeout: typeof import('@vueuse/core')['useTimeout']
|
||||||
|
const useTimeoutFn: typeof import('@vueuse/core')['useTimeoutFn']
|
||||||
|
const useTimeoutPoll: typeof import('@vueuse/core')['useTimeoutPoll']
|
||||||
|
const useTimestamp: typeof import('@vueuse/core')['useTimestamp']
|
||||||
|
const useTitle: typeof import('@vueuse/core')['useTitle']
|
||||||
|
const useToggle: typeof import('@vueuse/core')['useToggle']
|
||||||
|
const useTransition: typeof import('@vueuse/core')['useTransition']
|
||||||
|
const useUrlSearchParams: typeof import('@vueuse/core')['useUrlSearchParams']
|
||||||
|
const useUserMedia: typeof import('@vueuse/core')['useUserMedia']
|
||||||
|
const useVModel: typeof import('@vueuse/core')['useVModel']
|
||||||
|
const useVModels: typeof import('@vueuse/core')['useVModels']
|
||||||
|
const useVibrate: typeof import('@vueuse/core')['useVibrate']
|
||||||
|
const useVirtualList: typeof import('@vueuse/core')['useVirtualList']
|
||||||
|
const useWakeLock: typeof import('@vueuse/core')['useWakeLock']
|
||||||
|
const useWebNotification: typeof import('@vueuse/core')['useWebNotification']
|
||||||
|
const useWebSocket: typeof import('@vueuse/core')['useWebSocket']
|
||||||
|
const useWebWorker: typeof import('@vueuse/core')['useWebWorker']
|
||||||
|
const useWebWorkerFn: typeof import('@vueuse/core')['useWebWorkerFn']
|
||||||
|
const useWindowFocus: typeof import('@vueuse/core')['useWindowFocus']
|
||||||
|
const useWindowScroll: typeof import('@vueuse/core')['useWindowScroll']
|
||||||
|
const useWindowSize: typeof import('@vueuse/core')['useWindowSize']
|
||||||
|
const watch: typeof import('vue')['watch']
|
||||||
|
const watchArray: typeof import('@vueuse/core')['watchArray']
|
||||||
|
const watchAtMost: typeof import('@vueuse/core')['watchAtMost']
|
||||||
|
const watchDebounced: typeof import('@vueuse/core')['watchDebounced']
|
||||||
|
const watchEffect: typeof import('vue')['watchEffect']
|
||||||
|
const watchIgnorable: typeof import('@vueuse/core')['watchIgnorable']
|
||||||
|
const watchOnce: typeof import('@vueuse/core')['watchOnce']
|
||||||
|
const watchPausable: typeof import('@vueuse/core')['watchPausable']
|
||||||
|
const watchPostEffect: typeof import('vue')['watchPostEffect']
|
||||||
|
const watchSyncEffect: typeof import('vue')['watchSyncEffect']
|
||||||
|
const watchThrottled: typeof import('@vueuse/core')['watchThrottled']
|
||||||
|
const watchTriggerable: typeof import('@vueuse/core')['watchTriggerable']
|
||||||
|
const watchWithFilter: typeof import('@vueuse/core')['watchWithFilter']
|
||||||
|
const whenever: typeof import('@vueuse/core')['whenever']
|
||||||
|
}
|
18
frontend/components.d.ts
vendored
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
// generated by unplugin-vue-components
|
||||||
|
// We suggest you to commit this file into source control
|
||||||
|
// Read more: https://github.com/vuejs/core/pull/3399
|
||||||
|
import '@vue/runtime-core'
|
||||||
|
|
||||||
|
export {}
|
||||||
|
|
||||||
|
declare module '@vue/runtime-core' {
|
||||||
|
export interface GlobalComponents {
|
||||||
|
AppHeader: typeof import('./src/components/AppHeader.vue')['default']
|
||||||
|
'Icon:bx:bxMoon': typeof import('~icons/bx/bx-moon')['default']
|
||||||
|
'Icon:bx:bxsMoon': typeof import('~icons/bx/bxs-moon')['default']
|
||||||
|
'IconAkarIcons:githubFill': typeof import('~icons/akar-icons/github-fill')['default']
|
||||||
|
RouterLink: typeof import('vue-router')['RouterLink']
|
||||||
|
RouterView: typeof import('vue-router')['RouterView']
|
||||||
|
TextField: typeof import('./src/components/Form/TextField.vue')['default']
|
||||||
|
}
|
||||||
|
}
|
34
frontend/index.html
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en" data-theme="night">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>
|
||||||
|
Vitailse | Opinionated vite starter template with TailwindCSS
|
||||||
|
</title>
|
||||||
|
|
||||||
|
<meta
|
||||||
|
name="description"
|
||||||
|
content="Opinionated vite starter template with TailwindCSS"
|
||||||
|
/>
|
||||||
|
<link rel="icon" href="/favicon.ico" />
|
||||||
|
<link rel="icon" href="/favicon.png" type="image/png" />
|
||||||
|
<link
|
||||||
|
rel="alternate icon"
|
||||||
|
href="/favicon.ico"
|
||||||
|
type="image/png"
|
||||||
|
sizes="16x16"
|
||||||
|
/>
|
||||||
|
<link
|
||||||
|
rel="apple-touch-icon"
|
||||||
|
href="/apple-touch-icon.png"
|
||||||
|
sizes="180x180"
|
||||||
|
/>
|
||||||
|
<link rel="mask-icon" href="/favicon.png" color="#076AE0" />
|
||||||
|
<meta name="theme-color" content="#076AE0" />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="app"></div>
|
||||||
|
<script type="module" src="/src/main.ts"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
22
frontend/locales/en.json
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
{
|
||||||
|
"pages": {
|
||||||
|
"home": "Home",
|
||||||
|
"other": {
|
||||||
|
"menu": "Other Page",
|
||||||
|
"desc": "An example of other pages"
|
||||||
|
},
|
||||||
|
"not-found": "Page not found"
|
||||||
|
},
|
||||||
|
"app": {
|
||||||
|
"offline": "App ready to work offline",
|
||||||
|
"new-content": "New content available, click on reload button to update."
|
||||||
|
},
|
||||||
|
"intro": {
|
||||||
|
"desc": "Welcome to Vitailse, Vite starter template with {tailwindurl}",
|
||||||
|
"github": "Please give stars and report any issues on our {githuburl}"
|
||||||
|
},
|
||||||
|
"button": {
|
||||||
|
"reload": "Reload",
|
||||||
|
"close": "Close"
|
||||||
|
}
|
||||||
|
}
|
22
frontend/locales/id.json
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
{
|
||||||
|
"pages": {
|
||||||
|
"home": "Beranda",
|
||||||
|
"other": {
|
||||||
|
"menu": "Halaman lain",
|
||||||
|
"desc": "Contoh untuk halaman lain"
|
||||||
|
},
|
||||||
|
"not-found": "Laman tidak ditemukan"
|
||||||
|
},
|
||||||
|
"app": {
|
||||||
|
"offline": "Aplikasi siap digunakan tanpa jaringan internet",
|
||||||
|
"new-content": "Konten baru ditemukan, Tekan tombol perbarui untuk memperbarui laman."
|
||||||
|
},
|
||||||
|
"intro": {
|
||||||
|
"desc": "Selamat datang di Vitailse, Template awal vite dengan ",
|
||||||
|
"github": "Mohon berikan bintang dan laporkan masalah pada "
|
||||||
|
},
|
||||||
|
"button": {
|
||||||
|
"reload": "Perbarui",
|
||||||
|
"close": "Tutup"
|
||||||
|
}
|
||||||
|
}
|
69
frontend/package.json
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
{
|
||||||
|
"name": "@zynth/vitailse",
|
||||||
|
"description": "Vite starter template with TailwindCSS",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"main": "src/main.ts",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git+https://github.com/zynth17/vitailse.git"
|
||||||
|
},
|
||||||
|
"keywords": [
|
||||||
|
"vitailse",
|
||||||
|
"tailwindcss",
|
||||||
|
"vite",
|
||||||
|
"vitesse"
|
||||||
|
],
|
||||||
|
"author": "Christopher Reeeve",
|
||||||
|
"license": "MIT",
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://github.com/zynth17/vitailse/issues"
|
||||||
|
},
|
||||||
|
"homepage": "https://github.com/zynth17/vitailse#readme",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite",
|
||||||
|
"build": "vite-ssg build",
|
||||||
|
"serve": "vite preview",
|
||||||
|
"https-preview": "serve dist"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@tailwindcss/aspect-ratio": "^0.4.0",
|
||||||
|
"@tailwindcss/forms": "^0.5.2",
|
||||||
|
"@tailwindcss/typography": "^0.5.4",
|
||||||
|
"@types/node": "^18.0.4",
|
||||||
|
"@vueuse/components": "^8.9.3",
|
||||||
|
"@vueuse/core": "^8.9.3",
|
||||||
|
"@vueuse/head": "^0.7.6",
|
||||||
|
"autoprefixer": "^10.4.7",
|
||||||
|
"daisyui": "^2.24.0",
|
||||||
|
"pinia": "^2.0.16",
|
||||||
|
"postcss": "^8.4.14",
|
||||||
|
"tailwindcss": "^3.1.6",
|
||||||
|
"vue": "^3.2.37",
|
||||||
|
"vue-i18n": "^9.1.10",
|
||||||
|
"vue-router": "^4.1.2",
|
||||||
|
"workbox": "^0.0.0",
|
||||||
|
"workbox-window": "^6.5.3"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@iconify/json": "^2.1.78",
|
||||||
|
"@intlify/vite-plugin-vue-i18n": "^5.0.0",
|
||||||
|
"@vitejs/plugin-vue": "^3.0.0",
|
||||||
|
"@vue/compiler-sfc": "^3.2.37",
|
||||||
|
"@vue/server-renderer": "^3.2.37",
|
||||||
|
"critters": "^0.0.16",
|
||||||
|
"https-localhost": "^4.7.1",
|
||||||
|
"typescript": "^4.7.4",
|
||||||
|
"unplugin-auto-import": "^0.9.3",
|
||||||
|
"unplugin-icons": "^0.14.7",
|
||||||
|
"unplugin-vue-components": "0.21.1",
|
||||||
|
"unplugin-vue-router": "^0.0.21",
|
||||||
|
"vite": "^3.0.0",
|
||||||
|
"vite-plugin-pwa": "^0.12.3",
|
||||||
|
"vite-plugin-vue-layouts": "^0.7.0",
|
||||||
|
"vite-plugin-vue-type-imports": "^0.2.0",
|
||||||
|
"vite-ssg": "^0.20.2",
|
||||||
|
"vite-ssg-sitemap": "^0.3.2",
|
||||||
|
"vitest": "^0.18.0",
|
||||||
|
"vue-tsc": "^0.38.5"
|
||||||
|
}
|
||||||
|
}
|
5409
frontend/pnpm-lock.yaml
Normal file
6
frontend/postcss.config.js
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
module.exports = {
|
||||||
|
plugins: {
|
||||||
|
tailwindcss: {},
|
||||||
|
autoprefixer: {},
|
||||||
|
},
|
||||||
|
}
|
BIN
frontend/public/apple-touch-icon.png
Normal file
After Width: | Height: | Size: 7.7 KiB |
BIN
frontend/public/favicon-16x16.png
Normal file
After Width: | Height: | Size: 647 B |
BIN
frontend/public/favicon-32x32.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
frontend/public/favicon.ico
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
frontend/public/pwa-192x192.png
Normal file
After Width: | Height: | Size: 8.7 KiB |
BIN
frontend/public/pwa-512x512.png
Normal file
After Width: | Height: | Size: 22 KiB |
2
frontend/public/robots.txt
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
User-agent: *
|
||||||
|
Allow: /
|
1
frontend/public/site.webmanifest
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"}
|
3
frontend/src/App.vue
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
<template>
|
||||||
|
<router-view />
|
||||||
|
</template>
|
7
frontend/src/__test__/basic.spec.ts
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
import { describe, expect, it } from 'vitest'
|
||||||
|
|
||||||
|
describe('tests', () => {
|
||||||
|
it('should works', () => {
|
||||||
|
expect(1 + 1).toEqual(2)
|
||||||
|
})
|
||||||
|
})
|
BIN
frontend/src/assets/logo.png
Normal file
After Width: | Height: | Size: 35 KiB |
80
frontend/src/components/AppHeader.vue
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
defineEmits(['toggleSidebar']);
|
||||||
|
|
||||||
|
const { availableLocales } = useI18n();
|
||||||
|
|
||||||
|
const preferedDark = usePreferredDark();
|
||||||
|
const isDark = useStorage('isDark', preferedDark.value);
|
||||||
|
const body = ref<HTMLBodyElement | null>(null);
|
||||||
|
|
||||||
|
const toggleDarkMode = () => {
|
||||||
|
if (body.value) {
|
||||||
|
if (isDark.value) {
|
||||||
|
body.value.classList.remove('dark');
|
||||||
|
} else {
|
||||||
|
body.value.classList.add('dark');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
isDark.value = !isDark.value;
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
await nextTick();
|
||||||
|
|
||||||
|
body.value = document.querySelector('body') as HTMLBodyElement;
|
||||||
|
if (body.value) {
|
||||||
|
if (isDark.value) body.value.classList.add('dark');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<header>
|
||||||
|
<nav
|
||||||
|
class="
|
||||||
|
w-full
|
||||||
|
bg-white
|
||||||
|
text-gray-800
|
||||||
|
dark:bg-gray-800 dark:text-white
|
||||||
|
py-4
|
||||||
|
px-8
|
||||||
|
shadow-md
|
||||||
|
dark:shadow-md
|
||||||
|
flex
|
||||||
|
items-center
|
||||||
|
border-b border-gray-400/50
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<router-link :to="{ name: 'home' }">
|
||||||
|
<div class="font-bold lg:text-xl md:text-lg text-md">Vitailse</div>
|
||||||
|
</router-link>
|
||||||
|
<div class="ml-auto flex items-center h-full">
|
||||||
|
<select
|
||||||
|
id="language"
|
||||||
|
v-model="$i18n.locale"
|
||||||
|
class="py-1 focus:outline-none rounded dark:text-gray-800"
|
||||||
|
>
|
||||||
|
<option
|
||||||
|
v-for="locale in availableLocales"
|
||||||
|
:key="locale"
|
||||||
|
:value="locale"
|
||||||
|
>
|
||||||
|
{{ locale }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
<button
|
||||||
|
class="mx-5 cursor-pointer focus:outline-none"
|
||||||
|
@click="toggleDarkMode"
|
||||||
|
>
|
||||||
|
<icon:bx:bx-moon class="w-6 h-6" v-if="!isDark" />
|
||||||
|
<icon:bx:bxs-moon class="w-6 h-6" v-else />
|
||||||
|
</button>
|
||||||
|
<a href="https://github.com/zynth17/vitailse">
|
||||||
|
<icon-akar-icons:github-fill />
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped></style>
|
31
frontend/src/components/Form/TextField.vue
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
<template>
|
||||||
|
<div class="form-control w-full">
|
||||||
|
<label class="label">
|
||||||
|
<span class="label-text">{{ label }}</span>
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
:type="type"
|
||||||
|
v-model="value"
|
||||||
|
class="input input-bordered w-full"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
const props = defineProps({
|
||||||
|
modelValue: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
type: {
|
||||||
|
type: String,
|
||||||
|
default: 'text',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const value = useVModel(props, 'modelValue');
|
||||||
|
</script>
|
8
frontend/src/env.d.ts
vendored
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
/// <reference types="vite/client" />
|
||||||
|
|
||||||
|
declare module '*.vue' {
|
||||||
|
import { DefineComponent } from 'vue'
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types
|
||||||
|
const component: DefineComponent<{}, {}, any>
|
||||||
|
export default component
|
||||||
|
}
|
5
frontend/src/layouts/404.vue
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
<template>
|
||||||
|
<main class="w-full min-h-screen bg-blue-100 grid place-items-center">
|
||||||
|
<router-view />
|
||||||
|
</main>
|
||||||
|
</template>
|
18
frontend/src/layouts/default.vue
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
<script setup lang="ts"></script>
|
||||||
|
<template>
|
||||||
|
<header>
|
||||||
|
<app-header />
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<main
|
||||||
|
class="
|
||||||
|
p-8
|
||||||
|
dark:bg-gray-800 dark:text-white
|
||||||
|
bg-white
|
||||||
|
text-gray-800
|
||||||
|
min-h-screen
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<router-view />
|
||||||
|
</main>
|
||||||
|
</template>
|
19
frontend/src/main.ts
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
import App from '@/App.vue';
|
||||||
|
import { ViteSSG } from 'vite-ssg';
|
||||||
|
|
||||||
|
import '@/styles/index.css';
|
||||||
|
import { ViteSetupModule } from './types/ViteSetupModule';
|
||||||
|
import { extendedRoutes } from '@/router';
|
||||||
|
|
||||||
|
export const createApp = ViteSSG(
|
||||||
|
App,
|
||||||
|
{ routes: extendedRoutes },
|
||||||
|
async ctx => {
|
||||||
|
Object.values(
|
||||||
|
import.meta.glob<{ install: ViteSetupModule }>('./modules/*.ts', {
|
||||||
|
eager: true,
|
||||||
|
})
|
||||||
|
).map(i => i.install?.(ctx));
|
||||||
|
},
|
||||||
|
{}
|
||||||
|
);
|
29
frontend/src/modules/i18n.ts
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
import { ViteSetupModule } from '@/types/ViteSetupModule';
|
||||||
|
import { createI18n } from 'vue-i18n';
|
||||||
|
|
||||||
|
// Import i18n resources
|
||||||
|
// https://vitejs.dev/guide/features.html#glob-import
|
||||||
|
|
||||||
|
// Don't need this? Try vitesse-lite: https://github.com/antfu/vitesse-lite
|
||||||
|
const messages = Object.fromEntries(
|
||||||
|
Object.entries(
|
||||||
|
import.meta.glob<{ default: any }>('../../locales/*.{y(a)?ml,json}', {
|
||||||
|
eager: true,
|
||||||
|
})
|
||||||
|
).map(([key, value]) => {
|
||||||
|
const isYamlOrJson = key.endsWith('.yaml') || key.endsWith('.json');
|
||||||
|
|
||||||
|
return [key.slice(14, isYamlOrJson ? -5 : -4), value.default];
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
export const install: ViteSetupModule = ({ app }) => {
|
||||||
|
const i18n = createI18n({
|
||||||
|
legacy: false,
|
||||||
|
locale: 'en',
|
||||||
|
messages,
|
||||||
|
globalInjection: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
app.use(i18n);
|
||||||
|
};
|
14
frontend/src/modules/pinia.ts
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
import { ViteSetupModule } from '@/types/ViteSetupModule';
|
||||||
|
import { createPinia } from 'pinia';
|
||||||
|
|
||||||
|
// Setup Pinia
|
||||||
|
// https://pinia.esm.dev/
|
||||||
|
export const install: ViteSetupModule = ({ isClient, initialState, app }) => {
|
||||||
|
const pinia = createPinia();
|
||||||
|
app.use(pinia);
|
||||||
|
// Refer to
|
||||||
|
// https://github.com/antfu/vite-ssg/blob/main/README.md#state-serialization
|
||||||
|
// for other serialization strategies.
|
||||||
|
if (isClient) pinia.state.value = initialState.pinia || {};
|
||||||
|
else initialState.pinia = pinia.state.value;
|
||||||
|
};
|
10
frontend/src/modules/pwa.ts
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
import { ViteSetupModule } from '@/types/ViteSetupModule';
|
||||||
|
|
||||||
|
export const install: ViteSetupModule = ({ isClient, router }) => {
|
||||||
|
if (!isClient) return;
|
||||||
|
|
||||||
|
router.isReady().then(async () => {
|
||||||
|
const { registerSW } = await import('virtual:pwa-register');
|
||||||
|
registerSW({ immediate: true });
|
||||||
|
});
|
||||||
|
};
|
19
frontend/src/pages/[...all].vue
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
useHead({
|
||||||
|
title: '404. Not Found',
|
||||||
|
});
|
||||||
|
const { t } = useI18n();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<h1 class="text-blue-500 font-extrabold flex flex-col text-center">
|
||||||
|
<span class="text-7xl">404.</span>
|
||||||
|
<span class="text-5xl mt-5">{{ t('pages.not-found') }}</span>
|
||||||
|
</h1>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<route lang="yaml">
|
||||||
|
name : not-found
|
||||||
|
meta:
|
||||||
|
layout: 404
|
||||||
|
</route>
|
157
frontend/src/pages/index.vue
Normal file
|
@ -0,0 +1,157 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import TextField from '@/components/Form/TextField.vue';
|
||||||
|
useHead({
|
||||||
|
title: 'Homebox | Organize and Tag Your Stuff',
|
||||||
|
});
|
||||||
|
|
||||||
|
const registerFields = [
|
||||||
|
{
|
||||||
|
label: "What's your name?",
|
||||||
|
value: '',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "What's your email?",
|
||||||
|
value: '',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Name your group',
|
||||||
|
value: '',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Set your password',
|
||||||
|
value: '',
|
||||||
|
type: 'password',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Confirm your password',
|
||||||
|
value: '',
|
||||||
|
type: 'password',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
function registerUser() {
|
||||||
|
// Print Values of registerFields
|
||||||
|
|
||||||
|
for (let i = 0; i < registerFields.length; i++) {
|
||||||
|
console.log(registerFields[i].label, registerFields[i].value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const loginFields = [
|
||||||
|
{
|
||||||
|
label: 'Email',
|
||||||
|
value: '',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Password',
|
||||||
|
value: '',
|
||||||
|
type: 'password',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const registerForm = ref(false);
|
||||||
|
|
||||||
|
function toggleLogin() {
|
||||||
|
registerForm.value = !registerForm.value;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<header class="sm:px-6 py-2 lg:p-14 sm:py-6">
|
||||||
|
<h2
|
||||||
|
class="mt-1 text-4xl font-bold tracking-tight text-gray-200 sm:text-5xl lg:text-6xl"
|
||||||
|
>
|
||||||
|
Homebox
|
||||||
|
</h2>
|
||||||
|
<p class="ml-1 text-lg text-gray-400">
|
||||||
|
Track, Organize, and Manage your Shit.
|
||||||
|
</p>
|
||||||
|
</header>
|
||||||
|
<div class="grid p-6 sm:place-items-center min-h-[50vh]">
|
||||||
|
<Transition name="slide-fade">
|
||||||
|
<form v-if="registerForm" @submit.prevent="registerUser">
|
||||||
|
<div
|
||||||
|
class="card w-max-[500px] md:w-[500px] bg-base-100 shadow-xl"
|
||||||
|
>
|
||||||
|
<div class="card-body">
|
||||||
|
<h2 class="card-title">Register</h2>
|
||||||
|
<TextField
|
||||||
|
v-for="field in registerFields"
|
||||||
|
v-model="field.value"
|
||||||
|
:label="field.label"
|
||||||
|
:key="field.label"
|
||||||
|
:type="field.type"
|
||||||
|
/>
|
||||||
|
<div class="card-actions justify-end">
|
||||||
|
<button type="submit" class="btn btn-primary mt-2">
|
||||||
|
Register
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="text-center mt-2">
|
||||||
|
<button @click="toggleLogin">Already a User? Login</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<div v-else>
|
||||||
|
<div
|
||||||
|
class="card w-max-[500px] md:w-[500px] bg-base-100 shadow-xl"
|
||||||
|
>
|
||||||
|
<div class="card-body">
|
||||||
|
<h2 class="card-title">Login</h2>
|
||||||
|
<TextField
|
||||||
|
v-for="field in loginFields"
|
||||||
|
v-model="field.value"
|
||||||
|
:label="field.label"
|
||||||
|
:key="field.label"
|
||||||
|
:type="field.type"
|
||||||
|
/>
|
||||||
|
<div class="card-actions justify-end mt-2">
|
||||||
|
<button class="btn btn-primary">Login</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="text-center mt-2">
|
||||||
|
<button @click="toggleLogin">Not a User? Register</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Transition>
|
||||||
|
</div>
|
||||||
|
<div class="min-w-full absolute bottom-0 z-[-1]">
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
preserveAspectRatio="none"
|
||||||
|
viewBox="0 0 1440 320"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fill="#0099ff9b"
|
||||||
|
fill-opacity="1"
|
||||||
|
d="M0,32L30,42.7C60,53,120,75,180,80C240,85,300,75,360,80C420,85,480,107,540,128C600,149,660,171,720,160C780,149,840,107,900,90.7C960,75,1020,85,1080,122.7C1140,160,1200,224,1260,234.7C1320,245,1380,203,1410,181.3L1440,160L1440,320L1410,320C1380,320,1320,320,1260,320C1200,320,1140,320,1080,320C1020,320,960,320,900,320C840,320,780,320,720,320C660,320,600,320,540,320C480,320,420,320,360,320C300,320,240,320,180,320C120,320,60,320,30,320L0,320Z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
<div class="bg-[#0099ff9b] flex-col flex min-h-[32vh]">
|
||||||
|
<div class="mt-auto mx-auto mb-8">
|
||||||
|
<p class="text-center text-gray-200">
|
||||||
|
© 2022 Contents. All Rights Reserved. Haybytes LLC
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<route lang="yaml">
|
||||||
|
name: home
|
||||||
|
</route>
|
||||||
|
|
||||||
|
<style lang="css">
|
||||||
|
.slide-fade-enter-active {
|
||||||
|
transition: all 0.2s ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slide-fade-enter-from,
|
||||||
|
.slide-fade-leave-to {
|
||||||
|
position: absolute;
|
||||||
|
transform: translateX(20px);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
</style>
|
17
frontend/src/router.ts
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
import {
|
||||||
|
createRouter,
|
||||||
|
createWebHistory,
|
||||||
|
createMemoryHistory,
|
||||||
|
} from '@vue-router';
|
||||||
|
|
||||||
|
import { setupLayouts } from 'virtual:generated-layouts';
|
||||||
|
export let extendedRoutes: any = null;
|
||||||
|
export const router = createRouter({
|
||||||
|
history: import.meta.env.SSR ? createMemoryHistory() : createWebHistory(),
|
||||||
|
// You don't need to pass the routes anymore,
|
||||||
|
// the plugin writes it for you 🤖
|
||||||
|
extendRoutes: routes => {
|
||||||
|
extendedRoutes = routes;
|
||||||
|
return setupLayouts(routes);
|
||||||
|
},
|
||||||
|
});
|
7
frontend/src/store/index.ts
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
import { defineStore } from 'pinia';
|
||||||
|
|
||||||
|
export const useStore = defineStore('store', {
|
||||||
|
state: () => ({
|
||||||
|
count: 0,
|
||||||
|
}),
|
||||||
|
});
|
3
frontend/src/styles/index.css
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
@tailwind base;
|
||||||
|
@tailwind components;
|
||||||
|
@tailwind utilities;
|
3
frontend/src/types/ViteSetupModule.ts
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
import { ViteSSGContext } from 'vite-ssg';
|
||||||
|
|
||||||
|
export type ViteSetupModule = (ctx: ViteSSGContext) => void;
|
16
frontend/tailwind.config.js
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
module.exports = {
|
||||||
|
content: ['./index.html', './src/**/*.{vue,js,ts,jsx,tsx}'],
|
||||||
|
darkMode: 'class', // or 'media' or 'class'
|
||||||
|
theme: {
|
||||||
|
extend: {},
|
||||||
|
},
|
||||||
|
variants: {
|
||||||
|
extend: {},
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
require('@tailwindcss/forms'),
|
||||||
|
require('@tailwindcss/aspect-ratio'),
|
||||||
|
require('@tailwindcss/typography'),
|
||||||
|
require('daisyui'),
|
||||||
|
],
|
||||||
|
};
|
34
frontend/tsconfig.json
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "esnext",
|
||||||
|
"useDefineForClassFields": true,
|
||||||
|
"module": "esnext",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"strict": true,
|
||||||
|
"jsx": "preserve",
|
||||||
|
"sourceMap": true,
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"lib": ["esnext", "dom"],
|
||||||
|
"baseUrl": ".",
|
||||||
|
"paths": {
|
||||||
|
"@/*": ["src/*"]
|
||||||
|
},
|
||||||
|
"types": [
|
||||||
|
"vite/client",
|
||||||
|
"vite-plugin-vue-layouts/client",
|
||||||
|
"unplugin-icons/types/vue",
|
||||||
|
"vite-plugin-pwa/client",
|
||||||
|
"@intlify/vite-plugin-vue-i18n/client"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"src/**/*.ts",
|
||||||
|
"src/**/*.d.ts",
|
||||||
|
"src/**/*.tsx",
|
||||||
|
"src/**/*.vue",
|
||||||
|
"components.d.ts",
|
||||||
|
"auto-imports.d.ts",
|
||||||
|
"typed-router.d.ts"
|
||||||
|
]
|
||||||
|
}
|
95
frontend/typed-router.d.ts
vendored
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
|
||||||
|
// Generated by unplugin-vue-router. ‼️ DO NOT MODIFY THIS FILE ‼️
|
||||||
|
// It's recommended to commit this file.
|
||||||
|
// Make sure to add this file to your tsconfig.json file as an "includes" or "files" entry.
|
||||||
|
|
||||||
|
/// <reference types="unplugin-vue-router/client" />
|
||||||
|
|
||||||
|
import type {
|
||||||
|
// type safe route locations
|
||||||
|
RouteLocationTypedList,
|
||||||
|
RouteLocationResolvedTypedList,
|
||||||
|
RouteLocationNormalizedTypedList,
|
||||||
|
RouteLocationNormalizedLoadedTypedList,
|
||||||
|
|
||||||
|
// helper types
|
||||||
|
// route definitions
|
||||||
|
RouteRecordInfo,
|
||||||
|
ParamValue,
|
||||||
|
ParamValueOneOrMore,
|
||||||
|
ParamValueZeroOrMore,
|
||||||
|
ParamValueZeroOrOne,
|
||||||
|
|
||||||
|
// vue-router extensions
|
||||||
|
_RouterTyped,
|
||||||
|
RouterLinkTyped,
|
||||||
|
NavigationGuard,
|
||||||
|
UseLinkFnTyped,
|
||||||
|
} from 'unplugin-vue-router'
|
||||||
|
|
||||||
|
declare module '@vue-router/routes' {
|
||||||
|
export interface RouteNamedMap {
|
||||||
|
'home': RouteRecordInfo<'home', '/', Record<never, never>, Record<never, never>>,
|
||||||
|
'not-found': RouteRecordInfo<'not-found', '/:all(.*)', { all: ParamValue<true> }, { all: ParamValue<false> }>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module '@vue-router' {
|
||||||
|
import type { RouteNamedMap } from '@vue-router/routes'
|
||||||
|
|
||||||
|
export type RouterTyped = _RouterTyped<RouteNamedMap>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Type safe version of `RouteLocationNormalized` (the type of `to` and `from` in navigation guards).
|
||||||
|
* Allows passing the name of the route to be passed as a generic.
|
||||||
|
*/
|
||||||
|
export type RouteLocationNormalized<Name extends keyof RouteNamedMap = keyof RouteNamedMap> = RouteLocationNormalizedTypedList<RouteNamedMap>[Name]
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Type safe version of `RouteLocationNormalizedLoaded` (the return type of `useRoute()`).
|
||||||
|
* Allows passing the name of the route to be passed as a generic.
|
||||||
|
*/
|
||||||
|
export type RouteLocationNormalizedLoaded<Name extends keyof RouteNamedMap = keyof RouteNamedMap> = RouteLocationNormalizedLoadedTypedList<RouteNamedMap>[Name]
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Type safe version of `RouteLocationResolved` (the returned route of `router.resolve()`).
|
||||||
|
* Allows passing the name of the route to be passed as a generic.
|
||||||
|
*/
|
||||||
|
export type RouteLocationResolved<Name extends keyof RouteNamedMap = keyof RouteNamedMap> = RouteLocationResolvedTypedList<RouteNamedMap>[Name]
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Type safe version of `RouteLocation` . Allows passing the name of the route to be passed as a generic.
|
||||||
|
*/
|
||||||
|
export type RouteLocation<Name extends keyof RouteNamedMap = keyof RouteNamedMap> = RouteLocationTypedList<RouteNamedMap>[Name]
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a type safe params for a route location. Requires the name of the route to be passed as a generic.
|
||||||
|
*/
|
||||||
|
export type RouteParams<Name extends keyof RouteNamedMap> = RouteNamedMap[Name]['params']
|
||||||
|
/**
|
||||||
|
* Generate a type safe raw params for a route location. Requires the name of the route to be passed as a generic.
|
||||||
|
*/
|
||||||
|
export type RouteParamsRaw<Name extends keyof RouteNamedMap> = RouteNamedMap[Name]['paramsRaw']
|
||||||
|
|
||||||
|
export function useRouter(): RouterTyped
|
||||||
|
export function useRoute<Name extends keyof RouteNamedMap = keyof RouteNamedMap>(name?: Name): RouteLocationNormalizedLoadedTypedList<RouteNamedMap>[Name]
|
||||||
|
|
||||||
|
export const useLink: UseLinkFnTyped<RouteNamedMap>
|
||||||
|
|
||||||
|
export function onBeforeRouteLeave(guard: NavigationGuard<RouteNamedMap>): void
|
||||||
|
export function onBeforeRouteUpdate(guard: NavigationGuard<RouteNamedMap>): void
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module 'vue-router' {
|
||||||
|
import type { RouteNamedMap } from '@vue-router/routes'
|
||||||
|
|
||||||
|
export interface TypesConfig {
|
||||||
|
beforeRouteUpdate: NavigationGuard<RouteNamedMap>
|
||||||
|
beforeRouteLeave: NavigationGuard<RouteNamedMap>
|
||||||
|
|
||||||
|
$route: RouteLocationNormalizedLoadedTypedList<RouteNamedMap>[keyof RouteNamedMap]
|
||||||
|
$router: _RouterTyped<RouteNamedMap>
|
||||||
|
|
||||||
|
RouterLink: RouterLinkTyped<RouteNamedMap>
|
||||||
|
}
|
||||||
|
}
|
131
frontend/vite.config.ts
Normal file
|
@ -0,0 +1,131 @@
|
||||||
|
import { defineConfig } from 'vite';
|
||||||
|
import vue from '@vitejs/plugin-vue';
|
||||||
|
import { resolve } from 'path';
|
||||||
|
import Components from 'unplugin-vue-components/vite';
|
||||||
|
import AutoImport from 'unplugin-auto-import/vite';
|
||||||
|
import Icons from 'unplugin-icons/vite';
|
||||||
|
import IconsResolver from 'unplugin-icons/resolver';
|
||||||
|
import Layouts from 'vite-plugin-vue-layouts';
|
||||||
|
import { VitePWA } from 'vite-plugin-pwa';
|
||||||
|
import VueI18n from '@intlify/vite-plugin-vue-i18n';
|
||||||
|
import generateSitemap from 'vite-ssg-sitemap';
|
||||||
|
import VueRouter from 'unplugin-vue-router/vite';
|
||||||
|
import { VueRouterExports } from 'unplugin-vue-router';
|
||||||
|
// https://vitejs.dev/config/
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [
|
||||||
|
vue(),
|
||||||
|
VueRouter({
|
||||||
|
dts: true,
|
||||||
|
routesFolder: 'src/pages',
|
||||||
|
}),
|
||||||
|
Components({
|
||||||
|
dts: true,
|
||||||
|
resolvers: [
|
||||||
|
IconsResolver({
|
||||||
|
prefix: 'icon',
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
Icons({
|
||||||
|
compiler: 'vue3',
|
||||||
|
}),
|
||||||
|
AutoImport({
|
||||||
|
dts: true,
|
||||||
|
// targets to transform
|
||||||
|
include: [
|
||||||
|
/\.[tj]sx?$/, // .ts, .tsx, .js, .jsx
|
||||||
|
/\.vue\??/, // .vue
|
||||||
|
],
|
||||||
|
|
||||||
|
// global imports to register
|
||||||
|
imports: [
|
||||||
|
// presets
|
||||||
|
'vue',
|
||||||
|
{ '@vue-router': VueRouterExports },
|
||||||
|
'vue-i18n',
|
||||||
|
'@vueuse/core',
|
||||||
|
'@vueuse/head',
|
||||||
|
// custom
|
||||||
|
],
|
||||||
|
|
||||||
|
// custom resolvers
|
||||||
|
// see https://github.com/antfu/unplugin-auto-import/pull/23/
|
||||||
|
resolvers: [],
|
||||||
|
}),
|
||||||
|
Layouts(),
|
||||||
|
VitePWA({
|
||||||
|
includeAssets: [
|
||||||
|
'favicon-16x16.png',
|
||||||
|
'favicon-32x32.png',
|
||||||
|
'favicon.ico',
|
||||||
|
'robots.txt',
|
||||||
|
'apple-touch-icon.png',
|
||||||
|
],
|
||||||
|
manifest: {
|
||||||
|
name: 'Vitailse',
|
||||||
|
short_name: 'Vitailse',
|
||||||
|
description: 'Opinionated vite template with TailwindCSS',
|
||||||
|
theme_color: '#076AE0',
|
||||||
|
icons: [
|
||||||
|
{
|
||||||
|
src: 'pwa-192x192.png',
|
||||||
|
sizes: '192x192',
|
||||||
|
type: 'image/png',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
src: 'pwa-512x512.png',
|
||||||
|
sizes: '512x512',
|
||||||
|
type: 'image/png',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
src: 'pwa-512x512.png',
|
||||||
|
sizes: '512x512',
|
||||||
|
type: 'image/png',
|
||||||
|
purpose: 'any maskable',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
VueI18n({
|
||||||
|
runtimeOnly: true,
|
||||||
|
compositionOnly: true,
|
||||||
|
include: [resolve(__dirname, 'locales/**')],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
'@': resolve(__dirname, './src'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
server: {
|
||||||
|
fs: {
|
||||||
|
strict: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
optimizeDeps: {
|
||||||
|
include: ['vue', 'vue-router', '@vueuse/core', '@vueuse/head'],
|
||||||
|
},
|
||||||
|
// @ts-ignore
|
||||||
|
ssgOptions: {
|
||||||
|
script: 'async',
|
||||||
|
formatting: 'minify',
|
||||||
|
format: 'cjs',
|
||||||
|
onFinished() {
|
||||||
|
generateSitemap();
|
||||||
|
},
|
||||||
|
mock: true
|
||||||
|
},
|
||||||
|
// https://github.com/vitest-dev/vitest
|
||||||
|
test: {
|
||||||
|
include: ['src/__test__/**/*.test.ts', 'src/__test__/**/*.spec.ts'],
|
||||||
|
environment: 'jsdom',
|
||||||
|
deps: {
|
||||||
|
inline: ['@vue', '@vueuse', 'vue-demi'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ssr: {
|
||||||
|
// TODO: workaround until they support native ESM
|
||||||
|
noExternal: ['workbox-window', /vue-i18n/],
|
||||||
|
},
|
||||||
|
});
|