This commit is contained in:
2026-08-26 20:57:35 +03:00
commit e86085c2f0
41 changed files with 7921 additions and 0 deletions
+8
View File
@@ -0,0 +1,8 @@
[*.{js,jsx,mjs,cjs,ts,tsx,mts,cts,vue,css,scss,sass,less,styl}]
charset = utf-8
indent_size = 2
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true
end_of_line = lf
max_line_length = 100
+1
View File
@@ -0,0 +1 @@
* text=auto eol=lf
+39
View File
@@ -0,0 +1,39 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
.DS_Store
dist
dist-ssr
coverage
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
*.tsbuildinfo
.eslintcache
# Cypress
/cypress/videos/
/cypress/screenshots/
# Vitest
__screenshots__/
# Vite
*.timestamp-*-*.mjs
+10
View File
@@ -0,0 +1,10 @@
{
"$schema": "./node_modules/oxlint/configuration_schema.json",
"plugins": ["eslint", "typescript", "unicorn", "oxc", "vue"],
"env": {
"browser": true
},
"categories": {
"correctness": "error"
}
}
+6
View File
@@ -0,0 +1,6 @@
{
"$schema": "https://json.schemastore.org/prettierrc",
"semi": false,
"singleQuote": true,
"printWidth": 100
}
+9
View File
@@ -0,0 +1,9 @@
{
"recommendations": [
"Vue.volar",
"dbaeumer.vscode-eslint",
"EditorConfig.EditorConfig",
"oxc.oxc-vscode",
"esbenp.prettier-vscode"
]
}
+48
View File
@@ -0,0 +1,48 @@
# chat-app
This template should help get you started developing with Vue 3 in Vite.
## Recommended IDE Setup
[VS Code](https://code.visualstudio.com/) + [Vue (Official)](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur).
## Recommended Browser Setup
- Chromium-based browsers (Chrome, Edge, Brave, etc.):
- [Vue.js devtools](https://chromewebstore.google.com/detail/vuejs-devtools/nhdogjmejiglipccpnnnanhbledajbpd)
- [Turn on Custom Object Formatter in Chrome DevTools](http://bit.ly/object-formatters)
- Firefox:
- [Vue.js devtools](https://addons.mozilla.org/en-US/firefox/addon/vue-js-devtools/)
- [Turn on Custom Object Formatter in Firefox DevTools](https://fxdx.dev/firefox-devtools-custom-object-formatters/)
## Type Support for `.vue` Imports in TS
TypeScript cannot handle type information for `.vue` imports by default, so we replace the `tsc` CLI with `vue-tsc` for type checking. In editors, we need [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) to make the TypeScript language service aware of `.vue` types.
## Customize configuration
See [Vite Configuration Reference](https://vite.dev/config/).
## Project Setup
```sh
npm install
```
### Compile and Hot-Reload for Development
```sh
npm run dev
```
### Type-Check, Compile and Minify for Production
```sh
npm run build
```
### Lint with [ESLint](https://eslint.org/)
```sh
npm run lint
```
Vendored
+1
View File
@@ -0,0 +1 @@
/// <reference types="vite/client" />
+26
View File
@@ -0,0 +1,26 @@
import { globalIgnores } from 'eslint/config'
import { defineConfigWithVueTs, vueTsConfigs } from '@vue/eslint-config-typescript'
import pluginVue from 'eslint-plugin-vue'
import pluginOxlint from 'eslint-plugin-oxlint'
import skipFormatting from 'eslint-config-prettier/flat'
// To allow more languages other than `ts` in `.vue` files, uncomment the following lines:
// import { configureVueProject } from '@vue/eslint-config-typescript'
// configureVueProject({ scriptLangs: ['ts', 'tsx'] })
// More info at https://github.com/vuejs/eslint-config-typescript/#advanced-setup
export default defineConfigWithVueTs(
{
name: 'app/files-to-lint',
files: ['**/*.{vue,ts,mts,tsx}'],
},
globalIgnores(['**/dist/**', '**/dist-ssr/**', '**/coverage/**']),
...pluginVue.configs['flat/essential'],
vueTsConfigs.recommended,
...pluginOxlint.buildFromOxlintConfigFile('.oxlintrc.json'),
skipFormatting,
)
+13
View File
@@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="">
<head>
<meta charset="UTF-8">
<link rel="icon" href="/favicon.ico">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Vite App</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>
+6879
View File
File diff suppressed because it is too large Load Diff
+52
View File
@@ -0,0 +1,52 @@
{
"name": "ulitka-app",
"version": "0.0.1",
"private": true,
"type": "module",
"scripts": {
"dev": "vite",
"build": "run-p type-check \"build-only {@}\" --",
"preview": "vite preview",
"build-only": "vite build",
"type-check": "vue-tsc --build",
"lint": "run-s \"lint:*\"",
"lint:oxlint": "oxlint . --fix",
"lint:eslint": "eslint . --fix --cache",
"format": "prettier --write --experimental-cli src/"
},
"dependencies": {
"@fontsource/roboto": "^5.3.0",
"@vueuse/core": "^14.4.0",
"better-auth": "^1.7.1",
"pinia": "^4.0.3",
"vue": "^3.5.41",
"vue-router": "^5.2.0",
"vuetify": "^4.1.11"
},
"devDependencies": {
"@mdi/font": "^7.4.47",
"@tsconfig/node24": "^24.0.5",
"@types/node": "^26.3.0",
"@vitejs/plugin-vue": "^6.0.8",
"@vue/eslint-config-typescript": "^14.9.0",
"@vue/tsconfig": "^0.9.1",
"eslint": "^10.9.1",
"eslint-config-prettier": "^10.1.8",
"eslint-plugin-oxlint": "~1.80.0",
"eslint-plugin-vue": "~10.10.0",
"jiti": "^2.7.0",
"npm-run-all2": "^9.0.3",
"oxlint": "~1.80.0",
"prettier": "3.9.6",
"sass-embedded": "^1.103.1",
"typescript": "~7.0.2",
"unplugin-fonts": "^2.0.0",
"vite": "^8.2.2",
"vite-plugin-vue-devtools": "^8.2.1",
"vue-eslint-parser": "^10.4.1",
"vue-tsc": "^3.3.11"
},
"engines": {
"node": "^22.18.0 || >=24.12.0"
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

+40
View File
@@ -0,0 +1,40 @@
<script setup lang="ts">
import { useEventSource } from '@vueuse/core'
import { computed } from 'vue'
import { useRoute } from 'vue-router'
const route = useRoute()
// const { status, data, error, close } = useEventSource(
// 'https://event-source-url',
// ['notice', 'update'],
// { autoReconnect: {
// retries: 3,
// delay: 1000,
// onFailed() {
// alert('Failed to connect EventSource after 3 retries')
// },
// }, autoConnect: false },
// )
// console.log(data, status, error, close)
const layout = computed(() => {
const layout = route?.meta?.layout
return layout ? `${layout}Layout` : 'DefaultLayout'
})
</script>
<template>
<!-- <h1>You did it!</h1>-->
<!-- <router-view />-->
<component :is="layout">
<router-view v-slot="{ Component, route }">
<div :key="route.name">
<component :is="Component" />
</div>
</router-view>
</component>
</template>
<style scoped></style>
+9
View File
@@ -0,0 +1,9 @@
/*@import "tailwindcss";*/
html {
font-size: 16px;
font-weight: 400;
font-family: "Montserrat", sans-serif;
font-optical-sizing: auto;
font-style: normal;
}
+21
View File
@@ -0,0 +1,21 @@
<script setup lang="ts">
const menu = [
{ id: 1, title: 'home', to: '/' },
{ id: 2, title: 'issues', to: '/issues' },
{ id: 3, title: 'summary', to: '/summary' },
{ id: 4, title: 'reports', to: '/reports' },
]
</script>
<template>
<v-list class="mt-1" width="200" density="compact">
<v-list-item v-for="item in menu" :key="item.id" :to="item.to">
<v-list-item-title class="d-flex align-center ga-4">
<v-icon icon="mdi-information-outline" />
<div>{{ item.title }}</div>
</v-list-item-title>
</v-list-item>
</v-list>
</template>
<style scoped lang="scss"></style>
+26
View File
@@ -0,0 +1,26 @@
<script setup lang="ts">
import { ref } from 'vue'
const text = ref<string | null>(null)
async function onSearch() {
console.log('search')
}
</script>
<template>
<div>
<v-text-field
v-model="text"
clearable
variant="outlined"
density="compact"
placeholder="Search name..."
hide-details
@keydown.enter.prevent="onSearch"
@click:clear="onSearch"
/>
</div>
</template>
<style scoped lang="scss"></style>
+48
View File
@@ -0,0 +1,48 @@
<script setup lang="ts">
import { type Status, useStatusStore } from '@/stores/status.ts'
import { ref, watchEffect } from 'vue'
interface Props {
value?: string
}
const statusStore = useStatusStore()
const model = defineModel<string>()
const { value } = defineProps<Props>()
const statuses = ref<Status[]>([])
watchEffect(async () => {
statuses.value = await statusStore.list()
})
const select = (value: unknown) => {
if (Array.isArray(value)) {
model.value = value[0]
}
}
</script>
<template>
<div class="menu">
<v-menu>
<template v-slot:activator="{ props }">
<v-btn density="compact" v-bind="props" :text="model" />
</template>
<v-list density="compact" select-strategy="single-leaf" @update:selected="select">
<v-list-item v-for="el in statuses" :key="el.id" :value="el.name">
<v-list-item-title>{{ el.name }}</v-list-item-title>
</v-list-item>
</v-list>
</v-menu>
</div>
</template>
<style scoped lang="scss">
.menu {
width: 8rem;
overflow: hidden;
}
</style>
+13
View File
@@ -0,0 +1,13 @@
<script setup lang="ts"></script>
<template>
<v-layout>
<v-main>
<v-container :fluid="true">
<slot></slot>
</v-container>
</v-main>
</v-layout>
</template>
<style scoped lang="scss"></style>
+73
View File
@@ -0,0 +1,73 @@
<script setup lang="ts">
import { useRoute, useRouter } from 'vue-router'
import AppMenu from '@/components/AppMenu.vue'
const router = useRouter()
const title = 'Ulitka'
const version = '0.0.1'
function logout() {
console.log('logout')
}
function goToLogin() {
router.push({ path: '/login' })
}
function goToRegister() {
router.push({ path: '/register' })
}
function goToProfile() {
router.push({ path: '/profile' })
}
</script>
<template>
<v-layout>
<v-app-bar density="compact" elevation="0" border>
<template v-slot:prepend>
<v-menu>
<template v-slot:activator="{ props }">
<v-app-bar-nav-icon variant="text" rounded="0" v-bind="props"></v-app-bar-nav-icon>
</template>
<app-menu />
</v-menu>
</template>
<v-app-bar-title>
<div class="d-flex ga-2 align-center">
<span>{{ title }}</span>
<!-- <span class="text-label-small">ver: {{ version }}</span>-->
<span>v{{ version }}</span>
</div>
</v-app-bar-title>
<v-btn @click="goToLogin">Login</v-btn>
<v-btn @click="goToRegister">Register</v-btn>
<v-divider class="mx-1" vertical />
<v-menu>
<template v-slot:activator="{ props }">
<v-btn rounded="0" icon v-bind="props">
<v-icon icon="mdi-account-outline" />
</v-btn>
</template>
<v-list class="mt-1" width="200">
<v-list-item @click="goToProfile">Profile</v-list-item>
<v-list-item @click="logout">Logout</v-list-item>
</v-list>
</v-menu>
</v-app-bar>
<v-main>
<v-container :fluid="true">
<slot></slot>
</v-container>
</v-main>
</v-layout>
</template>
<style scoped lang="scss"></style>
+20
View File
@@ -0,0 +1,20 @@
import type { App, Component } from 'vue'
/**
* Register layouts in the app.go instance
*
* @param {App<Element>} app
*/
export function registerLayouts(app: App<Element>) {
const modules = import.meta.glob<{ default: Component }>('@/layouts/*.vue', {
eager: true,
})
Object.entries(modules).forEach(([key, component]) => {
const componentName = key.split('/').at(-1)
if (componentName) {
app.component(componentName.replace('.vue', ''), component.default)
}
})
}
+45
View File
@@ -0,0 +1,45 @@
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'
import router from './router'
import './assets/main.css'
import 'unfonts.css'
import 'vuetify/styles'
import '@mdi/font/css/materialdesignicons.css'
import { createVuetify } from 'vuetify'
import * as components from 'vuetify/components'
import * as directives from 'vuetify/directives'
import { registerLayouts } from '@/layouts/register.ts'
const app = createApp(App)
app.use(createPinia())
app.use(router)
const vuetify = createVuetify({
components,
directives,
theme: {
defaultTheme: 'dark', // 'system' | 'light' | 'dark'
},
defaults: {
global: {},
VBtn: {
variant: 'tonal',
},
VTextField: {
variant: 'outlined',
density: 'compact',
hideDetails: true,
},
},
})
app.use(vuetify)
registerLayouts(app)
app.mount('#app')
+56
View File
@@ -0,0 +1,56 @@
import type { Status } from '@/stores/status.ts'
export const user = []
export const account = []
export const company = []
export const statusListMock: Status[] = [
{ id: '1', name: 'new' },
{ id: '2', name: 'progress' },
{ id: '3', name: 'done' },
{ id: '4', name: 'closed' },
{ id: '5', name: 'canceled' },
]
export const issueListMock = [
{
id: '1',
key: 'AT-111',
title: 'title1',
text: 'test 1',
status: 'new',
priority: 1,
assignee: '1',
reporter: '2',
account_id: '1',
created_at: '',
tags: ['aa', 'bb'],
},
{
id: '2',
key: 'AT-112',
title: 'title2',
text: 'test 2',
status: 'working',
priority: 1,
assignee: '1',
reporter: '2',
account_id: '1',
created_at: '',
tags: ['aa', 'bb'],
},
]
export const type = [
{ id: '1', name: 'type one' },
{ id: '2', name: 'type two' },
{ id: '3', name: 'type three' },
]
export const message = []
export const chat = []
export const history = []
+42
View File
@@ -0,0 +1,42 @@
import { createRouter, createWebHistory } from 'vue-router'
import LoginView from '@/views/LoginView.vue'
import ProfileView from '@/views/ProfileView.vue'
import { issuesRoutes } from '@/router/issues.ts'
import { reportsRoutes } from '@/router/reports.ts'
import { summaryRoutes } from '@/router/summary.ts'
import RegisterView from '@/views/RegisterView.vue'
import HomeView from '@/views/HomeView.vue'
declare module 'vue-router' {
interface RouteMeta {
layout?: 'Blank' | 'Default'
}
}
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{
path: '/',
component: HomeView,
meta: { layout: 'Default' },
},
{
path: '/login',
component: LoginView,
},
{
path: '/register',
component: RegisterView,
},
{
path: '/profile',
component: ProfileView,
},
issuesRoutes,
summaryRoutes,
reportsRoutes,
],
})
export default router
+7
View File
@@ -0,0 +1,7 @@
import type { RouteRecordRaw } from 'vue-router'
import IssuesView from '@/views/IssuesView.vue'
export const issuesRoutes: RouteRecordRaw = {
path: '/issues',
component: IssuesView
}
+7
View File
@@ -0,0 +1,7 @@
import type { RouteRecordRaw } from 'vue-router'
import ReportsView from '@/views/ReportsView.vue'
export const reportsRoutes: RouteRecordRaw = {
path: '/reports',
component: ReportsView,
}
+7
View File
@@ -0,0 +1,7 @@
import type { RouteRecordRaw } from 'vue-router'
import SummaryView from '@/views/SummaryView.vue'
export const summaryRoutes: RouteRecordRaw = {
path: '/summary',
component: SummaryView,
}
+12
View File
@@ -0,0 +1,12 @@
import { ref, computed } from 'vue'
import { defineStore } from 'pinia'
export const useCounterStore = defineStore('counter', () => {
const count = ref(0)
const doubleCount = computed(() => count.value * 2)
function increment() {
count.value++
}
return { count, doubleCount, increment }
})
+38
View File
@@ -0,0 +1,38 @@
import { defineStore } from 'pinia'
import { computed, ref } from 'vue'
import { issueListMock } from '@/mocks.ts'
export interface Item {
id: string
key: string
title: string
text: string
status: string
priority: number
assignee: string
reporter: string
account_id: string
created_at: string
tags: string[]
}
export const useIssueStore = defineStore('item', () => {
const load = ref<Item[]>(issueListMock)
async function list(params: { page: number; itemsPerPage: number; text: string | null }) {
const { page, itemsPerPage, text } = params
return new Promise<{ items: Item[]; total: number }>((resolve) => {
const start = (page - 1) * itemsPerPage
const end = start + itemsPerPage
const items = load.value.slice()
const filtered = text ? items.filter((el) => el.text.includes(text)) : items
const paginated = filtered.slice(start, end === -1 ? undefined : end)
resolve({ items: paginated, total: filtered.length })
})
}
return { list }
})
+19
View File
@@ -0,0 +1,19 @@
import { defineStore } from 'pinia'
import { computed, ref } from 'vue'
import { statusListMock } from '@/mocks.ts'
export interface Status {
id: string
name: string
}
export const useStatusStore = defineStore('counter', () => {
const statuses = ref<Status[]>(statusListMock)
async function list(): Promise<Status[]> {
console.log('useStatusStore.list')
return Promise.resolve(statuses.value)
}
return { list }
})
+5
View File
@@ -0,0 +1,5 @@
<script setup lang="ts"></script>
<template>test</template>
<style scoped lang="scss"></style>
+112
View File
@@ -0,0 +1,112 @@
<script setup lang="ts">
import { ref, watch, watchEffect } from 'vue'
import { type Item, useIssueStore } from '@/stores/issue'
import { useRoute, useRouter } from 'vue-router'
import IssueFilter from '@/components/IssueFilter.vue'
import StatusMenu from '@/components/StatusMenu.vue'
const issueStore = useIssueStore()
const route = useRoute()
const router = useRouter()
const itemsPerPage = ref(10)
const page = ref(1)
const text = ref<string | null>(null)
const serverItems = ref<Item[]>([])
const loading = ref(true)
const totalItems = ref(0)
async function fetch() {
syncQuery()
loading.value = true
const res = await issueStore.list({
page: page.value,
itemsPerPage: itemsPerPage.value,
text: text.value,
})
serverItems.value = res.items
totalItems.value = res.total
loading.value = false
}
async function loadItems(params: { page: number; itemsPerPage: number; sortBy?: unknown }) {
page.value = params.page
itemsPerPage.value = params.itemsPerPage
await fetch()
}
async function onSearch() {
page.value = 1
await fetch()
}
function syncQuery() {
const query: Record<string, string> = {
page: String(page.value),
}
if (text.value) query.text = text.value
router.replace({ query })
}
watch(
() => route.query,
(query) => {
page.value = query.page ? Number(query.page) : 1
text.value = query.text ? String(query.text) : null
},
{ immediate: true },
)
watchEffect(async () => {
await loadItems({ page: 1, itemsPerPage: 10, sortBy: 1 })
})
const click = () => {
console.log('click')
}
</script>
<template>
<div class="d-flex flex-column ga-2">
<issue-filter />
<v-list border rounded density="compact" :lines="false">
<v-list-item v-for="el in serverItems" :key="el.id" @click.stop="click">
<template v-slot:title>
<div>{{ el.key }}</div>
</template>
<template v-slot:prepend>
<v-icon icon="mdi-square" />
</template>
<template v-slot:append>
<div class="d-flex ga-2">
<!-- <div>{{el.status}}</div>-->
<!-- <v-btn density="compact">btn</v-btn>-->
<status-menu v-model="el.status" />
<!-- <v-menu>-->
<!-- <template v-slot:activator="{ props }">-->
<!-- <v-btn variant="tonal" density="compact" v-bind="props"> Dropdown </v-btn>-->
<!-- </template>-->
<!-- <v-list density="compact">-->
<!-- <v-list-item v-for="(item, index) in [{title: 'menu 1'}]" :key="index" :value="index">-->
<!-- <v-list-item-title>{{ item.title }}</v-list-item-title>-->
<!-- </v-list-item>-->
<!-- </v-list>-->
<!-- </v-menu>-->
</div>
</template>
</v-list-item>
<v-list-item class="bg-black">
<v-btn density="compact" variant="text" prepend-icon="mdi-plus">create</v-btn>
</v-list-item>
</v-list>
</div>
</template>
+18
View File
@@ -0,0 +1,18 @@
<script setup lang="ts"></script>
<template>
<v-row>
<v-col cols="4" offset="4">
<div class="d-flex flex-column ga-2">
<div class="text-uppercase">login</div>
<v-text-field prepend-inner-icon="mdi-email" />
<v-text-field prepend-inner-icon="mdi-lock" />
<div>
<v-btn>login</v-btn>
</div>
</div>
</v-col>
</v-row>
</template>
<style scoped lang="scss"></style>
+5
View File
@@ -0,0 +1,5 @@
<script setup lang="ts"></script>
<template>profile</template>
<style scoped lang="scss"></style>
+18
View File
@@ -0,0 +1,18 @@
<script setup lang="ts"></script>
<template>
<v-row>
<v-col cols="4" offset="4">
<div class="d-flex flex-column ga-2">
<div class="text-uppercase">register</div>
<v-text-field prepend-inner-icon="mdi-email" />
<v-text-field prepend-inner-icon="mdi-lock" />
<div>
<v-btn>register</v-btn>
</div>
</div>
</v-col>
</v-row>
</template>
<style scoped lang="scss"></style>
+96
View File
@@ -0,0 +1,96 @@
<script setup lang="ts">
import { ref, watch, watchEffect } from 'vue'
import { type Item, useIssueStore } from '@/stores/issue'
import { useRoute, useRouter } from 'vue-router'
const issueStore = useIssueStore()
const route = useRoute()
const router = useRouter()
const itemsPerPage = ref(10)
const page = ref(1)
const text = ref<string | null>(null)
const serverItems = ref<Item[]>([])
const loading = ref(true)
const totalItems = ref(0)
async function fetch() {
syncQuery()
loading.value = true
const res = await issueStore.list({
page: page.value,
itemsPerPage: itemsPerPage.value,
text: text.value,
})
serverItems.value = res.items
totalItems.value = res.total
loading.value = false
}
async function loadItems(params: { page: number; itemsPerPage: number; sortBy?: unknown }) {
page.value = params.page
itemsPerPage.value = params.itemsPerPage
await fetch()
}
async function onSearch() {
page.value = 1
await fetch()
}
function syncQuery() {
const query: Record<string, string> = {
page: String(page.value),
}
if (text.value) query.text = text.value
router.replace({ query })
}
watch(
() => route.query,
(query) => {
page.value = query.page ? Number(query.page) : 1
text.value = query.text ? String(query.text) : null
},
{ immediate: true },
)
</script>
<template>
<div class="d-flex flex-column ga-2">
<v-text-field
v-model="text"
clearable
variant="outlined"
density="compact"
placeholder="Search name..."
hide-details
@keydown.enter.prevent="onSearch"
@click:clear="onSearch"
/>
<v-data-table-server
density="compact"
hide-default-header
hover
:headers="[
{ key: 'key', title: 'key' },
{ key: 'text', title: 'text' },
{ key: 'status', title: 'status' },
{ key: 'priority', title: 'priority' },
{ key: 'assignee', title: 'assignee' },
]"
:items-per-page="itemsPerPage"
:items-per-page-options="[itemsPerPage]"
:page="page"
:items="serverItems"
:items-length="totalItems"
item-value="text"
@update:options="loadItems"
/>
</div>
</template>
+5
View File
@@ -0,0 +1,5 @@
<script setup lang="ts"></script>
<template>summary</template>
<style scoped lang="scss"></style>
+18
View File
@@ -0,0 +1,18 @@
{
"extends": "@vue/tsconfig/tsconfig.dom.json",
"include": ["env.d.ts", "src/**/*", "src/**/*.vue"],
"exclude": ["src/**/__tests__/*"],
"compilerOptions": {
// Extra safety for array and object lookups, but may have false positives.
"noUncheckedIndexedAccess": true,
// Path mapping for cleaner imports.
"paths": {
"@/*": ["./src/*"]
},
// `vue-tsc --build` produces a .tsbuildinfo file for incremental type-checking.
// Specified here to keep it out of the root directory.
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo"
}
}
+11
View File
@@ -0,0 +1,11 @@
{
"files": [],
"references": [
{
"path": "./tsconfig.node.json"
},
{
"path": "./tsconfig.app.json"
}
]
}
+27
View File
@@ -0,0 +1,27 @@
// TSConfig for modules that run in Node.js environment via either transpilation or type-stripping.
{
"extends": "@tsconfig/node24/tsconfig.json",
"include": [
"vite.config.*",
"vitest.config.*",
"cypress.config.*",
"playwright.config.*",
"eslint.config.*"
],
"compilerOptions": {
// Most tools use transpilation instead of Node.js's native type-stripping.
// Bundler mode provides a smoother developer experience.
"module": "preserve",
"moduleResolution": "bundler",
// Include Node.js types and avoid accidentally including other `@types/*` packages.
"types": ["node"],
// Disable emitting output during `vue-tsc --build`, which is used for type-checking only.
"noEmit": true,
// `vue-tsc --build` produces a .tsbuildinfo file for incremental type-checking.
// Specified here to keep it out of the root directory.
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo"
}
}
+31
View File
@@ -0,0 +1,31 @@
import { fileURLToPath, URL } from 'node:url'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import vueDevTools from 'vite-plugin-vue-devtools'
import ViteFonts from 'unplugin-fonts/vite'
// https://vite.dev/config/
export default defineConfig({
server: { port: 3333 },
plugins: [
vue(),
vueDevTools(),
ViteFonts({
fontsource: {
families: [
{
name: 'Roboto',
weights: [100, 300, 400, 500, 700, 900],
styles: ['normal', 'italic'],
},
],
},
}),
],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url)),
},
},
})