Second working version with vite-express integration.

Replace Bootstrap with Privemue.
Add i18n support. English and Brazilian Portuguese to start.
New UI with Primevue. Use tabbed menu, nice theme, etc..
WIP with Steam login.
This commit is contained in:
Daniel Ceregatti 2024-07-29 07:54:16 -07:00
parent 85e59ae8c6
commit 53dea0f45d
23 changed files with 553 additions and 173 deletions

View file

@ -19,7 +19,7 @@ services:
args: args:
- USER_ID - USER_ID
user: ${USER_ID} user: ${USER_ID}
hostname: web hostname: dayzdockerserver
volumes: volumes:
- homedir_main:/home/user - homedir_main:/home/user
- serverfiles:/serverfiles - serverfiles:/serverfiles

View file

@ -7,12 +7,12 @@ trap '
' SIGINT SIGTERM ' SIGINT SIGTERM
# Set PS1 so we know we're in the container # Set PS1 so we know we're in the container
if grep -q "dz-web" .bashrc if ! grep -q "dz-web" .bashrc
then then
echo "Adding PS1 to .bashrc..." echo "Adding PS1 to .bashrc..."
cat >> .bashrc <<EOF cat >> .bashrc <<EOF
alias ls='ls --color' alias ls='ls --color'
export PS1="${debian_chroot:+($debian_chroot)}\u@dz-web:\w\$ " export PS1='${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ '
unset DEVELOPMENT unset DEVELOPMENT
export PATH=${PATH}:/usr/local/dotnet export PATH=${PATH}:/usr/local/dotnet
EOF EOF

View file

@ -1,7 +1,6 @@
<script setup> <script setup>
import Error from '@/components/Error.vue' import Error from '@/components/Error.vue'
import Login from '@/components/Login.vue' import Header from '@/components/Header.vue'
import Main from '@/components/Main.vue'
import { useFetch } from '@vueuse/core' import { useFetch } from '@vueuse/core'
import { useAppStore } from '@/store.js' import { useAppStore } from '@/store.js'
const store = useAppStore() const store = useAppStore()
@ -17,8 +16,7 @@ useFetch('/status', {
<Suspense> <Suspense>
<main> <main>
<Error /> <Error />
<Login v-if="! store.steamStatus.loggedIn" /> <Header />
<Main v-else />
</main> </main>
</Suspense> </Suspense>
</template> </template>

View file

@ -1,9 +0,0 @@
<script setup>
import Mods from '@/components/Mods.vue'
import SearchResults from "@/components/SearchResults.vue";
</script>
<template>
<SearchResults />
<Mods />
</template>

View file

@ -1,40 +1,11 @@
<script setup> <script setup>
import { watch } from 'vue' import Dialog from 'primevue/dialog'
import { Modal } from 'bootstrap'
import { useAppStore } from '@/store.js' import { useAppStore } from '@/store.js'
const store = useAppStore() const store = useAppStore()
let modal = {}
watch(() => store.errorText, () => {
modal = new Modal('#errorModal', {})
if (store.errorText) {
modal.show()
}
})
</script> </script>
<template> <template>
<div <Dialog v-model:visible="store.errorText" modal :header="$t('Error')" :style="{ width: '25rem' }">
class="modal" {{ store.errorText }}
id="errorModal" </Dialog>
data-bs-backdrop="static"
data-bs-keyboard="false"
tabindex="-1"
aria-labelledby="errorModalLabel"
aria-hidden="true"
>
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h1 class="modal-title fs-5" id="errorModalLabel">Error</h1>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
{{ store.errorText }}
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
</template> </template>

View file

@ -0,0 +1,19 @@
<script setup>
import Button from 'primevue/button'
import { useFetch } from '@vueuse/core'
import { useAppStore } from '@/store.js'
const store = useAppStore()
async function base() {
let which = '/installbase'
if (store.steamStatus.installed) {
which = '/updatebase'
}
const { data } = await useFetch(which).get().json()
store.errorText = data.value.message
}
</script>
<template>
<Button @click="base" v-if="store.steamStatus.installed">{{ store.steamStatus.installed ? $t('Update Server Files') : $t('Install Server Files') }}</Button>
<span v-else>{{ $t('Server files not installed') }}</span>
</template>

View file

@ -1,45 +1,50 @@
<script setup> <script setup>
import Search from '@/components/Search.vue' import Tabs from 'primevue/tabs'
import Status from '@/components/Status.vue' import TabList from 'primevue/tablist'
import Tab from 'primevue/tab'
import TabPanels from 'primevue/tabpanels'
import TabPanel from 'primevue/tabpanel'
import Files from '@/components/Files.vue'
import Home from '@/components/Home.vue'
import Steam from '@/components/Steam.vue'
import Servers from '@/components/Servers.vue' import Servers from '@/components/Servers.vue'
import { useFetch } from '@vueuse/core' import Mods from '@/components/Mods.vue'
import { useAppStore } from '@/store.js' import Status from '@/components/Status.vue'
const store = useAppStore()
const set = (w, e) => {
store.section = w
const active = Array.from(document.getElementsByClassName('active'))
active.forEach((a) => a.classList.remove('active'))
e.target.classList.add('active')
}
async function base() {
let which = '/installbase'
if (store.steamStatus.installed) {
which = '/updatebase'
}
const { data } = await useFetch(which).get().json()
store.errorText = data.value.message
}
</script> </script>
<template> <template>
<div v-if="store.steamStatus" class="row"> <div class="grid">
<div class="col-3 text-center"> <div class="col-8 col-offset-2 text-center">
<h1>DayZ Docker Server</h1> <Tabs value="0">
</div> <TabList>
<div class="col-5"> <Tab value="0">{{ $t('Home') }}</Tab>
<button <Tab value="1">{{ $t('Steam') }}</Tab>
@click="base" <Tab value="2">{{ $t('Files') }}</Tab>
class="btn btn-sm btn-success" <Tab value="3">{{ $t('Mods') }}</Tab>
> <Tab value="4">{{ $t('Servers') }}</Tab>
{{ store.steamStatus.installed ? "Update" : "Install" }} Server Files <Tab value="5">{{ $t('Status') }}</Tab>
</button> </TabList>
<button @click="updatemods" class="btn btn-sm btn-outline-success">Update Mods</button> <TabPanels>
<button type="button" @click="set('servers', $event)" class="btn btn-sm btn-outline-primary">Servers</button> <TabPanel value="0">
<button type="button" @click="set('mods', $event)" class="btn btn-sm btn-outline-primary active" data-bs-toggle="button">Mods</button> <Home />
<button type="button" @click="set('search', $event)" class="btn btn-sm btn-outline-primary">Search</button> </TabPanel>
</div> <TabPanel value="1">
<Search /> <Steam />
<Status /> </TabPanel>
<Servers /> <TabPanel value="2">
</div> <Files />
</TabPanel>
<TabPanel value="3">
<Mods />
</TabPanel>
<TabPanel value="4">
<Servers />
</TabPanel>
<TabPanel value="5">
<Status />
</TabPanel>
</TabPanels>
</Tabs>
</div>
</div>
</template> </template>

View file

@ -0,0 +1,8 @@
<script setup>
</script>
<template>
<div class="">
<h1>{{ $t('DayZ Docker Server') }}</h1>
</div>
</template>

View file

@ -1,34 +0,0 @@
<script setup>
</script>
<template>
<div class="middle">
<h1>Login to Steam</h1>
<form name="form" method="POST" action="/login" enctype="application/x-www-form-urlencoded">
<div>
<input type="text" name="username" placeholder="Username" />
</div>
<div>
<input type="password" name="password" placeholder="Password" />
</div>
<div>
<input type="text" name="guard" placeholder="Steam Guard" />
</div>
<div>
<input type="checkbox" name="remember" id="remember" />
<label for="remember">Remember me</label>
</div>
<div>
<button type="submit" name="submit">Login</button>
</div>
</form>
</div>
</template>
<style scoped>
.middle {
margin: 0 auto;
width: 50%;
text-align: center;
}
</style>

View file

@ -4,8 +4,6 @@ import Header from './Header.vue'
</script> </script>
<template> <template>
<div class="container-fluid min-vh-100 d-flex flex-column bg-light"> <Header />
<Header /> <!-- <Body />-->
<Body />
</div>
</template> </template>

View file

@ -4,7 +4,7 @@ import { useFetch } from '@vueuse/core'
import { useAppStore } from '@/store.js' import { useAppStore } from '@/store.js'
import ModInfo from '@/components/Modinfo.vue' import ModInfo from '@/components/Modinfo.vue'
const store = useAppStore() const store = useAppStore()
const { data, error } = useFetch(config.baseUrl + '/mods', { const { data, error } = useFetch('/mods', {
afterFetch(ctx) { afterFetch(ctx) {
store.mods = ctx.data.mods store.mods = ctx.data.mods
return ctx return ctx
@ -13,14 +13,14 @@ const { data, error } = useFetch(config.baseUrl + '/mods', {
</script> </script>
<template> <template>
<div class="row flex-grow-1" v-if="store.section === 'mods'"> <div class="row flex-grow-1">
<div v-if="error" class="row text-danger"> <div v-if="error" class="row text-danger">
{{ error }} {{ error }}
</div> </div>
<div v-if="store.mods.length === 0">No mods are installed</div> <div v-if="store.mods.length === 0">{{ $t('No mods are installed') }}</div>
<div v-else class="col-md-3 border" v-if="data"> <div v-else class="col-md-3 border" v-if="data">
<div> <div>
<h4 class="text-center">Installed Mods</h4> <h4 class="text-center">{{ $t('Installed Mods') }}</h4>
<table> <table>
<tr> <tr>
<th>Steam Link</th> <th>Steam Link</th>

View file

@ -1,20 +1,13 @@
<script setup> <script setup>
import { useAppStore } from '@/store.js' import { useAppStore} from '@/store.js'
const store = useAppStore() const store = useAppStore()
</script> </script>
<template> <template>
<div class="row" v-if="store.section === 'servers'"> <div v-if="store.servers.length > 0">
<div class="col text-center"> <h1>{{ $t('Servers') }}</h1>
<div class="row"> </div>
<div class="col"> <div v-else>
<h2>Servers</h2> {{ $t('No servers have been created') }}
</div> </div>
</div>
<div class="row flex-grow-1">
<div class="col-6 justify-content-end">Running</div>
<div class="col-6">Stopped</div>
</div>
</div>
</div>
</template> </template>

View file

@ -1,4 +1,5 @@
<script setup> <script setup>
import Button from 'primevue/button'
import { useAppStore } from '@/store.js' import { useAppStore } from '@/store.js'
const store = useAppStore() const store = useAppStore()
</script> </script>
@ -6,18 +7,21 @@ const store = useAppStore()
<template> <template>
<div class="col"> <div class="col">
<div> <div>
Logged into Steam: {{ $t('Logged into Steam') }}:
<span v-if="store.steamStatus.loggedIn" class="bi bi-check h2 text-success"></span> <span v-if="store.steamStatus.loggedIn" class="pi pi-check" style="color: green"></span>
<span v-else class="bi bi-x h2 danger text-danger"></span> <span v-else class="pi pi-times" style="color: red"></span>
</div> </div>
<div> <div>
Server files installed: {{ $t('Server files installed') }}:
<span v-if="store.steamStatus.installed" class="bi bi-check h2 text-success"></span> <span v-if="store.steamStatus.installed" class="pi pi-check" style="color: green"></span>
<span v-else class="bi bi-x h2 danger text-danger"></span> <span v-else class="pi pi-times" style="color: red"></span>
</div> </div>
<div v-if="store.steamStatus.version"> <div v-if="store.steamStatus.version">
Version: <span class="text-success fw-bold">{{ store.steamStatus.version }}</span> {{ $t('Version') }}: <span style="color: green;">{{ store.steamStatus.version }}</span>
<span class="text-success fw-bold">({{ store.steamStatus.appid }})</span> <span class="bold">({{ store.steamStatus.appid }})</span>
</div> </div>
<div>
<Button @click="store.errorText = $t('This is an error message')">{{ $t('Test error') }}</Button>
</div>
</div> </div>
</template> </template>

View file

@ -0,0 +1,61 @@
<script setup>
import { useFetch } from '@vueuse/core'
import Checkbox from 'primevue/checkbox'
import Button from 'primevue/button'
import InputText from 'primevue/inputtext'
import Password from 'primevue/password'
import { useAppStore } from '@/store.js'
const store = useAppStore()
async function logOut() {
const response = await useFetch('/logout')
if (response.ok) {
store.steamStatus.loggedIn = false
} else if (response.err) {
store.errorText = response.err
}
}
function submit(e) {
console.log("Submit")
}
let username = ''
let password = ''
let remember = false
let steamGuardCode = ''
</script>
<template>
<div v-if="store.steamStatus.loggedIn">
<div>{{ $t('Already logged in to steam') }}</div>
<div>
<Button @click="logOut">{{ $t('Log out') }}</Button>
</div>
</div>
<div class="grid">
<div class="col-6 col-offset-3">
<div class="flex flex-column gap-2">
{{ $t('There are no saved Steam credentials. To install the server files and mods, please login to Steam') }}
</div>
<div class="">
</div>
<div class="flex flex-column gap-2">
<label for="username">{{ $t('Username') }}</label>
<InputText id="username" v-model="username" />
</div>
<div class="flex flex-column gap-2">
<label for="password">{{ $t('Password') }}</label>
<Password id="username" v-model="password" :feedback="false" toggleMask />
</div>
<div class="flex flex-column gap-2">
<label for="steamGuardCode">{{ $t('Steam Guard Code') }}</label>
<InputText id="steamGuardCode" v-model="steamGuardCode" />
</div>
<div class="flex flex-column gap-2">
<label for="remember">{{ $t('Remember Credentials') }}</label>
<Checkbox v-model="remember" />
</div>
<div class="flex flex-column gap-2">
<Button type="submit" onclick="submit()">{{ $t('Submit') }}</Button>
</div>
</div>
</div>
</template>

View file

@ -1,10 +1,6 @@
button { .p-tablist-tab-list {
padding: 5px; display: flex;
margin: 10px; justify-content: center;
}
th, td {
padding-right: 10px
} }
.active { .active {

View file

@ -0,0 +1,42 @@
export const en = {
'Already logged in to steam': 'Already logged in to steam',
'DayZ': 'DayZ',
'DayZ Docker Server': 'DayZ Docker Server',
'Error': 'Error',
'Experimental': 'Experimental',
'Files': 'Files',
'Home': 'Home',
'Install': 'Install',
'Installed mods': 'Installed mods',
'Install Server Files': 'Install Server Files',
'Log out': 'Log out',
'Logged into Steam': 'Logged into Steam',
'Mods': 'Mods',
'Name': 'Name',
'No mods are installed': 'No mods are installed',
'No servers have been created': 'No servers have been created',
'Not Installed': 'Not Installed',
'Password': 'Password',
'Release': 'Release',
'Remember Credentials': 'Remember Credentials',
'Search': 'Search',
'Server files installed': 'Server files installed',
'Server files not installed': 'Server files not installed',
'Servers': 'Servers',
'Stable': 'Stable',
'Status': 'Status',
'Steam': 'Steam',
'Steam Guard Code': 'Steam Guard Code',
'Submit': 'Submit',
'Test error': 'Test error',
'There are no saved Steam credentials. To install the server files and mods, please login to Steam': 'There are no saved Steam credentials. To install the server files and mods, please login to Steam',
'This is an error message': 'This is an error message',
'Total servers': 'Total servers',
'Total workshop items': 'Total workshop items',
'Up to date': 'Up to date',
'Update': 'Update',
'Update Server Files': 'Update Server Files',
'Username': 'Username',
'Version': 'Version',
'Workshop': 'Workshop',
}

View file

@ -0,0 +1,16 @@
import { en } from './en.js'
import { pt } from './pt.js'
const messages = {
en: en,
pt: pt,
}
const locales = {
legacy: false,
locale: 'en',
fallbackLocale: 'en',
messages: messages
}
export { locales }

View file

@ -0,0 +1,42 @@
export const pt = {
'Already logged in to steam': 'Já registrado com Steam',
'DayZ': 'DayZ',
'DayZ Docker Server': 'Servidor DayZ de Docker',
'Error': 'Erro',
'Experimental': 'Experimental',
'Files': 'Arquivos',
'Home': 'Início',
'Install': 'Instalar',
'Installed mods': 'Mods instalados',
'Install Server Files': 'Instalar Arquivos de Servidor',
'Log out': 'Encerrar sessão',
'Logged into Steam': 'Registrado com Steam',
'Mods': 'Mods',
'Name': 'Nome',
'No mods are installed': 'Nenhum mod está installado',
'No servers have been created': 'Nenhum servidor foi criado',
'Not Installed': 'Não Instalado',
'Password': 'Senha',
'Release': 'Lançado',
'Remember Credentials': 'Lembrar Credenciais',
'Search': 'Procurar',
'Server files installed': 'Arquivos de servidor instalado',
'Server files not installed': 'Arquivos de servidor não instalados',
'Servers': 'Servidores',
'Stable': 'Estável',
'Status': 'Estado',
'Steam': 'Steam',
'Steam Guard Code': 'Còdigo Steam Guard',
'Submit': 'Enviar',
'Test error': 'Testar erro',
'There are no saved Steam credentials. To install the server files and mods, please login to Steam': 'Não existe credencias salvados. Para instalar o servidor, por favor registre-se no Steam',
'This is an error message': 'Esta é uma mensagem de erro',
'Total servers': 'Total de servidores',
'Total workshop items': 'Total de itens de oficina',
'Up to date': 'Atualizado',
'Update': 'Atualizar',
'Update Server Files': 'Atualizer Arquivos de Servidor',
'Username': 'Usuário',
'Version': 'Versão',
'Workshop': 'Oficina',
}

View file

@ -1,12 +1,15 @@
import 'bootstrap/dist/css/bootstrap.min.css' import 'primeicons/primeicons.css'
import 'bootstrap' import 'primeflex/primeflex.css'
import 'bootstrap-icons/font/bootstrap-icons.css'
import './css/index.css' import './css/index.css'
import { createApp } from 'vue' import { createApp } from 'vue'
import { createPinia } from 'pinia' import { createPinia } from 'pinia'
import App from './App.vue' import App from './App.vue'
import {useAppStore} from '@/store' import { useAppStore } from '@/store'
import PrimeVue from 'primevue/config'
import Aura from '@primevue/themes/aura'
import { createI18n } from 'vue-i18n'
import { locales } from '@/locales'
// Create an instance of our Vue app // Create an instance of our Vue app
const app = createApp(App) const app = createApp(App)
@ -14,6 +17,10 @@ const app = createApp(App)
// Add the store // Add the store
app.use(createPinia()) app.use(createPinia())
// Add i18n
const i18n = createI18n(locales)
app.use(i18n)
// A global error handler // A global error handler
app.config.errorHandler = (err, instance, info) => { app.config.errorHandler = (err, instance, info) => {
const store = useAppStore() const store = useAppStore()
@ -21,5 +28,12 @@ app.config.errorHandler = (err, instance, info) => {
console.error('GLOBAL ERROR HANDLER! ', err, instance, info) console.error('GLOBAL ERROR HANDLER! ', err, instance, info)
} }
// Add PrimeVue
app.use(PrimeVue, {
theme: {
preset: Aura
}
})
// Mount it // Mount it
app.mount('#app') app.mount('#app')

View file

@ -2,13 +2,13 @@ import { defineStore } from 'pinia'
export const useAppStore = defineStore('app', { export const useAppStore = defineStore('app', {
state: () => ({ state: () => ({
errorText: '', errorText: false,
modId: 0, modId: 0,
modFile: '', modFile: false,
messageText: '', messageText: false,
mods: [], mods: [],
searchText: '', searchText: false,
section: 'mods', servers: [],
steamStatus: {appid: 0, installed: false, loggedIn: false, version: ''}, steamStatus: {appid: 0, installed: false, loggedIn: false, version: ''},
}) })
}) })

View file

@ -229,15 +229,27 @@ app.post(('/login'), async (req, res) => {
let args = `+login "${username}" "${password}"` let args = `+login "${username}" "${password}"`
if (guard) args += ` "${guard}"` if (guard) args += ` "${guard}"`
const result = await steamcmd(args) const result = await steamcmd(args)
console.log(result) if (remember) {
fs.writeFileSync(config.loginFile, username)
}
if (result) { if (result) {
fs.writeFileSync(config.loginFile, username) fs.writeFileSync(config.loginFile, username)
res.send(1) res.send({"ok": 1})
} else { } else {
res.send(0) res.send({"ok": 0})
} }
}) })
// Logout from Steam
app.get(('/logout'), async (req, res) => {
fs.unlink(config.loginFile, (err) => {
if (err) {
return res.send({"ok": 0, "error:": err})
}
res.send({"ok": 1})
})
})
// Get mod metadata by ID // Get mod metadata by ID
app.get('/mod/:modId', (req, res) => { app.get('/mod/:modId', (req, res) => {
const modId = req.params["modId"] const modId = req.params["modId"]

241
web/package-lock.json generated
View file

@ -10,11 +10,16 @@
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"@popperjs/core": "^2.11.8", "@popperjs/core": "^2.11.8",
"@primevue/themes": "^4.0.2",
"@vueuse/core": "^10.11.0", "@vueuse/core": "^10.11.0",
"bootstrap": "^5.3.3", "bootstrap": "^5.3.3",
"bootstrap-icons": "^1.11.3", "bootstrap-icons": "^1.11.3",
"pinia": "^2.2.0", "pinia": "^2.2.0",
"vue": "^3.4.34" "primeflex": "^3.3.1",
"primeicons": "^7.0.0",
"primevue": "^4.0.2",
"vue": "^3.4.34",
"vue-i18n": "^9.13.1"
}, },
"devDependencies": { "devDependencies": {
"@vitejs/plugin-vue": "^5.1.1", "@vitejs/plugin-vue": "^5.1.1",
@ -403,6 +408,47 @@
"node": ">=12" "node": ">=12"
} }
}, },
"node_modules/@intlify/core-base": {
"version": "9.13.1",
"resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-9.13.1.tgz",
"integrity": "sha512-+bcQRkJO9pcX8d0gel9ZNfrzU22sZFSA0WVhfXrf5jdJOS24a+Bp8pozuS9sBI9Hk/tGz83pgKfmqcn/Ci7/8w==",
"dependencies": {
"@intlify/message-compiler": "9.13.1",
"@intlify/shared": "9.13.1"
},
"engines": {
"node": ">= 16"
},
"funding": {
"url": "https://github.com/sponsors/kazupon"
}
},
"node_modules/@intlify/message-compiler": {
"version": "9.13.1",
"resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-9.13.1.tgz",
"integrity": "sha512-SKsVa4ajYGBVm7sHMXd5qX70O2XXjm55zdZB3VeMFCvQyvLew/dLvq3MqnaIsTMF1VkkOb9Ttr6tHcMlyPDL9w==",
"dependencies": {
"@intlify/shared": "9.13.1",
"source-map-js": "^1.0.2"
},
"engines": {
"node": ">= 16"
},
"funding": {
"url": "https://github.com/sponsors/kazupon"
}
},
"node_modules/@intlify/shared": {
"version": "9.13.1",
"resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-9.13.1.tgz",
"integrity": "sha512-u3b6BKGhE6j/JeRU6C/RL2FgyJfy6LakbtfeVF8fJXURpZZTzfh3e05J0bu0XPw447Q6/WUp3C4ajv4TMS4YsQ==",
"engines": {
"node": ">= 16"
},
"funding": {
"url": "https://github.com/sponsors/kazupon"
}
},
"node_modules/@jridgewell/sourcemap-codec": { "node_modules/@jridgewell/sourcemap-codec": {
"version": "1.5.0", "version": "1.5.0",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz",
@ -417,6 +463,63 @@
"url": "https://opencollective.com/popperjs" "url": "https://opencollective.com/popperjs"
} }
}, },
"node_modules/@primeuix/styled": {
"version": "0.0.5",
"resolved": "https://registry.npmjs.org/@primeuix/styled/-/styled-0.0.5.tgz",
"integrity": "sha512-pVoGn/uPkVm/DyF3TR3EmH/pL/dP4nR42FcYbVduFq9VfO3KVeOEqvcCULHXos66RZO9MCbCFUoLy6ctf9GUGQ==",
"dependencies": {
"@primeuix/utils": "^0.0.5"
},
"engines": {
"node": ">=12.11.0"
}
},
"node_modules/@primeuix/utils": {
"version": "0.0.5",
"resolved": "https://registry.npmjs.org/@primeuix/utils/-/utils-0.0.5.tgz",
"integrity": "sha512-ntUiUgtRtkF8KuaxHffzhYxQxoXk6LAPHm7CVlFjdqS8Rx8xRkLkZVyo84E+pO2hcNFkOGVP/GxHhQ2s94O8zA==",
"engines": {
"node": ">=12.11.0"
}
},
"node_modules/@primevue/core": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/@primevue/core/-/core-4.0.2.tgz",
"integrity": "sha512-SpCIQ1LG6B66cecmZt1UdFeY7tbdJrgnhncADolfE9fcCyTSBtbaVLLhCV2E4Q9myMwCFE5KQxqazA4rdLWk4w==",
"dependencies": {
"@primeuix/styled": "^0.0.5",
"@primeuix/utils": "^0.0.5"
},
"engines": {
"node": ">=12.11.0"
},
"peerDependencies": {
"vue": "^3.0.0"
}
},
"node_modules/@primevue/icons": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/@primevue/icons/-/icons-4.0.2.tgz",
"integrity": "sha512-S1VEpMsx4uUAsTjZtII03LQqgocYwSKbXMF0YDFWybY8r5LaHaveVZxS/i+KbsCLaL18BXJw0L/7L1eQdJHzaA==",
"dependencies": {
"@primeuix/utils": "^0.0.5",
"@primevue/core": "4.0.2"
},
"engines": {
"node": ">=12.11.0"
}
},
"node_modules/@primevue/themes": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/@primevue/themes/-/themes-4.0.2.tgz",
"integrity": "sha512-DGrdFOWRUb8/qDX+Hjkg0SkU2hvo6EmEE5/fA/+NSlLAemj5fcR8r3wo9jhFMIU0QVNl17jZycuL5taU0H72BA==",
"dependencies": {
"@primeuix/styled": "^0.0.5"
},
"engines": {
"node": ">=12.11.0"
}
},
"node_modules/@rollup/rollup-android-arm-eabi": { "node_modules/@rollup/rollup-android-arm-eabi": {
"version": "4.19.1", "version": "4.19.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.19.1.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.19.1.tgz",
@ -1737,6 +1840,30 @@
"node": "^10 || ^12 || >=14" "node": "^10 || ^12 || >=14"
} }
}, },
"node_modules/primeflex": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/primeflex/-/primeflex-3.3.1.tgz",
"integrity": "sha512-zaOq3YvcOYytbAmKv3zYc+0VNS9Wg5d37dfxZnveKBFPr7vEIwfV5ydrpiouTft8MVW6qNjfkaQphHSnvgQbpQ=="
},
"node_modules/primeicons": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/primeicons/-/primeicons-7.0.0.tgz",
"integrity": "sha512-jK3Et9UzwzTsd6tzl2RmwrVY/b8raJ3QZLzoDACj+oTJ0oX7L9Hy+XnVwgo4QVKlKpnP/Ur13SXV/pVh4LzaDw=="
},
"node_modules/primevue": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/primevue/-/primevue-4.0.2.tgz",
"integrity": "sha512-bf5In//ixosZDOcfJ0iaBXhtQSpNC/CuAOE2KHd/SgUKZdij3VRljQ+I9+Be/TTxfikIeKH7eZ4gzDbWiIZurw==",
"dependencies": {
"@primeuix/styled": "^0.0.5",
"@primeuix/utils": "^0.0.5",
"@primevue/core": "4.0.2",
"@primevue/icons": "4.0.2"
},
"engines": {
"node": ">=12.11.0"
}
},
"node_modules/proxy-addr": { "node_modules/proxy-addr": {
"version": "2.0.7", "version": "2.0.7",
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
@ -2191,6 +2318,25 @@
"optional": true "optional": true
} }
} }
},
"node_modules/vue-i18n": {
"version": "9.13.1",
"resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-9.13.1.tgz",
"integrity": "sha512-mh0GIxx0wPtPlcB1q4k277y0iKgo25xmDPWioVVYanjPufDBpvu5ySTjP5wOrSvlYQ2m1xI+CFhGdauv/61uQg==",
"dependencies": {
"@intlify/core-base": "9.13.1",
"@intlify/shared": "9.13.1",
"@vue/devtools-api": "^6.5.0"
},
"engines": {
"node": ">= 16"
},
"funding": {
"url": "https://github.com/sponsors/kazupon"
},
"peerDependencies": {
"vue": "^3.0.0"
}
} }
}, },
"dependencies": { "dependencies": {
@ -2360,6 +2506,29 @@
"dev": true, "dev": true,
"optional": true "optional": true
}, },
"@intlify/core-base": {
"version": "9.13.1",
"resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-9.13.1.tgz",
"integrity": "sha512-+bcQRkJO9pcX8d0gel9ZNfrzU22sZFSA0WVhfXrf5jdJOS24a+Bp8pozuS9sBI9Hk/tGz83pgKfmqcn/Ci7/8w==",
"requires": {
"@intlify/message-compiler": "9.13.1",
"@intlify/shared": "9.13.1"
}
},
"@intlify/message-compiler": {
"version": "9.13.1",
"resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-9.13.1.tgz",
"integrity": "sha512-SKsVa4ajYGBVm7sHMXd5qX70O2XXjm55zdZB3VeMFCvQyvLew/dLvq3MqnaIsTMF1VkkOb9Ttr6tHcMlyPDL9w==",
"requires": {
"@intlify/shared": "9.13.1",
"source-map-js": "^1.0.2"
}
},
"@intlify/shared": {
"version": "9.13.1",
"resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-9.13.1.tgz",
"integrity": "sha512-u3b6BKGhE6j/JeRU6C/RL2FgyJfy6LakbtfeVF8fJXURpZZTzfh3e05J0bu0XPw447Q6/WUp3C4ajv4TMS4YsQ=="
},
"@jridgewell/sourcemap-codec": { "@jridgewell/sourcemap-codec": {
"version": "1.5.0", "version": "1.5.0",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz",
@ -2370,6 +2539,45 @@
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
"integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==" "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A=="
}, },
"@primeuix/styled": {
"version": "0.0.5",
"resolved": "https://registry.npmjs.org/@primeuix/styled/-/styled-0.0.5.tgz",
"integrity": "sha512-pVoGn/uPkVm/DyF3TR3EmH/pL/dP4nR42FcYbVduFq9VfO3KVeOEqvcCULHXos66RZO9MCbCFUoLy6ctf9GUGQ==",
"requires": {
"@primeuix/utils": "^0.0.5"
}
},
"@primeuix/utils": {
"version": "0.0.5",
"resolved": "https://registry.npmjs.org/@primeuix/utils/-/utils-0.0.5.tgz",
"integrity": "sha512-ntUiUgtRtkF8KuaxHffzhYxQxoXk6LAPHm7CVlFjdqS8Rx8xRkLkZVyo84E+pO2hcNFkOGVP/GxHhQ2s94O8zA=="
},
"@primevue/core": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/@primevue/core/-/core-4.0.2.tgz",
"integrity": "sha512-SpCIQ1LG6B66cecmZt1UdFeY7tbdJrgnhncADolfE9fcCyTSBtbaVLLhCV2E4Q9myMwCFE5KQxqazA4rdLWk4w==",
"requires": {
"@primeuix/styled": "^0.0.5",
"@primeuix/utils": "^0.0.5"
}
},
"@primevue/icons": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/@primevue/icons/-/icons-4.0.2.tgz",
"integrity": "sha512-S1VEpMsx4uUAsTjZtII03LQqgocYwSKbXMF0YDFWybY8r5LaHaveVZxS/i+KbsCLaL18BXJw0L/7L1eQdJHzaA==",
"requires": {
"@primeuix/utils": "^0.0.5",
"@primevue/core": "4.0.2"
}
},
"@primevue/themes": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/@primevue/themes/-/themes-4.0.2.tgz",
"integrity": "sha512-DGrdFOWRUb8/qDX+Hjkg0SkU2hvo6EmEE5/fA/+NSlLAemj5fcR8r3wo9jhFMIU0QVNl17jZycuL5taU0H72BA==",
"requires": {
"@primeuix/styled": "^0.0.5"
}
},
"@rollup/rollup-android-arm-eabi": { "@rollup/rollup-android-arm-eabi": {
"version": "4.19.1", "version": "4.19.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.19.1.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.19.1.tgz",
@ -3286,6 +3494,27 @@
"source-map-js": "^1.2.0" "source-map-js": "^1.2.0"
} }
}, },
"primeflex": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/primeflex/-/primeflex-3.3.1.tgz",
"integrity": "sha512-zaOq3YvcOYytbAmKv3zYc+0VNS9Wg5d37dfxZnveKBFPr7vEIwfV5ydrpiouTft8MVW6qNjfkaQphHSnvgQbpQ=="
},
"primeicons": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/primeicons/-/primeicons-7.0.0.tgz",
"integrity": "sha512-jK3Et9UzwzTsd6tzl2RmwrVY/b8raJ3QZLzoDACj+oTJ0oX7L9Hy+XnVwgo4QVKlKpnP/Ur13SXV/pVh4LzaDw=="
},
"primevue": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/primevue/-/primevue-4.0.2.tgz",
"integrity": "sha512-bf5In//ixosZDOcfJ0iaBXhtQSpNC/CuAOE2KHd/SgUKZdij3VRljQ+I9+Be/TTxfikIeKH7eZ4gzDbWiIZurw==",
"requires": {
"@primeuix/styled": "^0.0.5",
"@primeuix/utils": "^0.0.5",
"@primevue/core": "4.0.2",
"@primevue/icons": "4.0.2"
}
},
"proxy-addr": { "proxy-addr": {
"version": "2.0.7", "version": "2.0.7",
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
@ -3577,6 +3806,16 @@
"resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.10.tgz", "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.10.tgz",
"integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==", "integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==",
"requires": {} "requires": {}
},
"vue-i18n": {
"version": "9.13.1",
"resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-9.13.1.tgz",
"integrity": "sha512-mh0GIxx0wPtPlcB1q4k277y0iKgo25xmDPWioVVYanjPufDBpvu5ySTjP5wOrSvlYQ2m1xI+CFhGdauv/61uQg==",
"requires": {
"@intlify/core-base": "9.13.1",
"@intlify/shared": "9.13.1",
"@vue/devtools-api": "^6.5.0"
}
} }
} }
} }

View file

@ -20,10 +20,15 @@
}, },
"dependencies": { "dependencies": {
"@popperjs/core": "^2.11.8", "@popperjs/core": "^2.11.8",
"@primevue/themes": "^4.0.2",
"@vueuse/core": "^10.11.0", "@vueuse/core": "^10.11.0",
"bootstrap": "^5.3.3", "bootstrap": "^5.3.3",
"bootstrap-icons": "^1.11.3", "bootstrap-icons": "^1.11.3",
"pinia": "^2.2.0", "pinia": "^2.2.0",
"vue": "^3.4.34" "primeflex": "^3.3.1",
"primeicons": "^7.0.0",
"primevue": "^4.0.2",
"vue": "^3.4.34",
"vue-i18n": "^9.13.1"
} }
} }