start migrating to tailwind
This commit is contained in:
parent
073d9dcba0
commit
23f46be962
@ -1,5 +1,4 @@
|
||||
@import '_variables.scss';
|
||||
@import 'bootstrap/scss/bootstrap';
|
||||
|
||||
#__nuxt,
|
||||
#__nuxt > div,
|
||||
@ -16,24 +15,24 @@ main > div {
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
font-family: 'Source Sans Pro', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial,
|
||||
sans-serif;
|
||||
background-color: $bg-primary;
|
||||
background: $bg-primary;
|
||||
font-size: 15px;
|
||||
word-spacing: 1px;
|
||||
-ms-text-size-adjust: 100%;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
// html,
|
||||
// body {
|
||||
// font-family: 'Source Sans Pro', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial,
|
||||
// sans-serif;
|
||||
// background-color: $bg-primary;
|
||||
// background: $bg-primary;
|
||||
// font-size: 15px;
|
||||
// word-spacing: 1px;
|
||||
// -ms-text-size-adjust: 100%;
|
||||
// -webkit-text-size-adjust: 100%;
|
||||
// -moz-osx-font-smoothing: grayscale;
|
||||
// -webkit-font-smoothing: antialiased;
|
||||
// box-sizing: border-box;
|
||||
// width: 100%;
|
||||
// height: 100%;
|
||||
// padding: 0;
|
||||
// margin: 0;
|
||||
// }
|
||||
|
||||
a {
|
||||
color: $link-color;
|
||||
@ -56,21 +55,6 @@ a:hover {
|
||||
--bs-list-group-border-radius: $b-radius;
|
||||
}
|
||||
|
||||
.alert-success {
|
||||
background-color: $success;
|
||||
color: $alert-color;
|
||||
}
|
||||
|
||||
.alert-danger {
|
||||
background-color: $danger;
|
||||
color: $alert-color;
|
||||
}
|
||||
|
||||
.alert-warning {
|
||||
background-color: $warning;
|
||||
color: $alert-color;
|
||||
}
|
||||
|
||||
.media-alert {
|
||||
position: absolute;
|
||||
top: 80px;
|
||||
|
15
components/Alert.vue
Normal file
15
components/Alert.vue
Normal file
@ -0,0 +1,15 @@
|
||||
<template>
|
||||
<div
|
||||
v-if="indexStore.showAlert"
|
||||
class="alert show alert-dismissible fade media-alert position-fixed"
|
||||
:class="indexStore.alertVariant"
|
||||
role="alert"
|
||||
>
|
||||
{{ indexStore.alertMsg }}
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close" @click="indexStore.showAlert=false"></button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const indexStore = useIndex()
|
||||
</script>
|
@ -1,91 +1,86 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="container">
|
||||
<h2 class="pb-4 pt-3">Channel Configuration</h2>
|
||||
<div class="w-100" style="height: 43px">
|
||||
<div class="float-end">
|
||||
<button class="btn btn-primary" @click="addChannel()">Add new Channel</button>
|
||||
</div>
|
||||
</div>
|
||||
<form
|
||||
v-if="configStore.configGui && configStore.configGui[configStore.configID]"
|
||||
@submit.prevent="onSubmitGui"
|
||||
>
|
||||
<div class="mb-3 row">
|
||||
<label for="configName" class="col-sm-2 col-form-label">Name</label>
|
||||
<div class="col-sm-10">
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
id="configName"
|
||||
v-model="configStore.configGui[configStore.configID].name"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3 row">
|
||||
<label for="configUrl" class="col-sm-2 col-form-label">Preview URL</label>
|
||||
<div class="col-sm-10">
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
id="configUrl"
|
||||
v-model="configStore.configGui[configStore.configID].preview_url"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3 row">
|
||||
<label for="configPath" class="col-sm-2 col-form-label">Config Path</label>
|
||||
<div class="col-sm-10">
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
id="configPath"
|
||||
v-model="configStore.configGui[configStore.configID].config_path"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3 row">
|
||||
<label for="configExtensions" class="col-sm-2 col-form-label">Extra Extensions</label>
|
||||
<div class="col-sm-10">
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
id="configExtensions"
|
||||
v-model="configStore.configGui[configStore.configID].extra_extensions"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3 row">
|
||||
<label for="configService" class="col-sm-2 col-form-label">Service</label>
|
||||
<div class="col-sm-10">
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
id="configService"
|
||||
v-model="configStore.configGui[configStore.configID].service"
|
||||
disabled
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-1" style="min-width: 158px">
|
||||
<div class="btn-group">
|
||||
<button class="btn btn-primary" type="submit">Save</button>
|
||||
<button
|
||||
class="btn btn-danger"
|
||||
v-if="
|
||||
configStore.configGui.length > 1 &&
|
||||
configStore.configGui[configStore.configID].id > 1
|
||||
"
|
||||
@click="deleteChannel()"
|
||||
>
|
||||
Delete
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<div class="w-full max-w-[800px]">
|
||||
<h2 class="pt-3 text-3xl">Channel Configuration</h2>
|
||||
<div class="w-full flex justify-end my-4">
|
||||
<button class="btn btn-sm btn-primary" @click="addChannel()">Add new Channel</button>
|
||||
</div>
|
||||
<form
|
||||
v-if="configStore.configGui && configStore.configGui[configStore.configID]"
|
||||
@submit.prevent="onSubmitGui"
|
||||
class="w-full"
|
||||
>
|
||||
<label class="form-control w-full">
|
||||
<div class="label">
|
||||
<span class="label-text">Name</span>
|
||||
</div>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Type here"
|
||||
class="input input-bordered w-full"
|
||||
v-model="configStore.configGui[configStore.configID].name"
|
||||
/>
|
||||
</label>
|
||||
|
||||
<label class="form-control w-full mt-5">
|
||||
<div class="label">
|
||||
<span class="label-text">Preview URL</span>
|
||||
</div>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Type here"
|
||||
class="input input-bordered w-full"
|
||||
v-model="configStore.configGui[configStore.configID].preview_url"
|
||||
/>
|
||||
</label>
|
||||
|
||||
<label class="form-control w-full mt-5">
|
||||
<div class="label">
|
||||
<span class="label-text">Config Path</span>
|
||||
</div>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Type here"
|
||||
class="input input-bordered w-full"
|
||||
v-model="configStore.configGui[configStore.configID].config_path"
|
||||
/>
|
||||
</label>
|
||||
|
||||
<label class="form-control w-full mt-5">
|
||||
<div class="label">
|
||||
<span class="label-text">Extra Extensions</span>
|
||||
</div>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Type here"
|
||||
class="input input-bordered w-full"
|
||||
v-model="configStore.configGui[configStore.configID].extra_extensions"
|
||||
/>
|
||||
</label>
|
||||
|
||||
<label class="form-control w-full mt-5">
|
||||
<div class="label">
|
||||
<span class="label-text">Service</span>
|
||||
</div>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Type here"
|
||||
class="input input-bordered w-full"
|
||||
v-model="configStore.configGui[configStore.configID].service"
|
||||
disabled
|
||||
/>
|
||||
</label>
|
||||
|
||||
<div class="join mt-4">
|
||||
<button class="join-item btn btn-primary" type="submit">Save</button>
|
||||
<button
|
||||
class="join-item btn btn-primary"
|
||||
v-if="configStore.configGui.length > 1 && configStore.configGui[configStore.configID].id > 1"
|
||||
@click="deleteChannel()"
|
||||
>
|
||||
Delete
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -121,7 +116,7 @@ async function onSubmitGui() {
|
||||
if (update.status) {
|
||||
indexStore.msgAlert('alert-success', 'Update GUI config success!', 2)
|
||||
} else {
|
||||
indexStore.msgAlert('alert-danger', 'Update GUI config failed!', 2)
|
||||
indexStore.msgAlert('alert-error', 'Update GUI config failed!', 2)
|
||||
}
|
||||
}
|
||||
|
||||
@ -147,7 +142,7 @@ async function deleteChannel() {
|
||||
if (response.status === 200) {
|
||||
indexStore.msgAlert('alert-success', 'Delete GUI config success!', 2)
|
||||
} else {
|
||||
indexStore.msgAlert('alert-danger', 'Delete GUI config failed!', 2)
|
||||
indexStore.msgAlert('alert-error', 'Delete GUI config failed!', 2)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -1,114 +1,89 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="container">
|
||||
<h2 class="pb-4 pt-3">Playout Configuration</h2>
|
||||
<form v-if="configStore.configPlayout" @submit.prevent="onSubmitPlayout">
|
||||
<div v-for="(item, key) in configStore.configPlayout" class="mb-2 row" :key="key">
|
||||
<div class="col-sm-1">
|
||||
<strong>{{ key }}:</strong>
|
||||
</div>
|
||||
<div class="col-sm-11 pb-4 mt-4">
|
||||
<div v-for="(prop, name) in (item as Record<string, any>)" class="mb-2 row">
|
||||
<label :for="name" class="col-sm-2 col-form-label">{{ name }}:</label>
|
||||
<div class="col-sm-10">
|
||||
<div v-if="name.toString() === 'help_text'" class="pt-2 pb-2">{{ prop }}</div>
|
||||
<input
|
||||
v-else-if="name.toString() === 'sender_pass'"
|
||||
type="password"
|
||||
class="form-control"
|
||||
:id="name"
|
||||
v-model="item[name]"
|
||||
/>
|
||||
<textarea
|
||||
v-else-if="
|
||||
name.toString() === 'output_param' || name.toString() === 'custom_filter'
|
||||
"
|
||||
class="form-control"
|
||||
:id="name"
|
||||
v-model="item[name]"
|
||||
rows="5"
|
||||
/>
|
||||
<input
|
||||
v-else-if="typeof prop === 'number' && prop % 1 === 0"
|
||||
type="number"
|
||||
class="form-control"
|
||||
:id="name"
|
||||
v-model="item[name]"
|
||||
style="max-width: 250px"
|
||||
/>
|
||||
<input
|
||||
v-else-if="typeof prop === 'number'"
|
||||
type="number"
|
||||
class="form-control"
|
||||
:id="name"
|
||||
v-model="item[name]"
|
||||
step="0.0001"
|
||||
style="max-width: 250px"
|
||||
/>
|
||||
<input
|
||||
v-else-if="typeof prop === 'boolean'"
|
||||
type="checkbox"
|
||||
class="form-check-input mt-2"
|
||||
:id="name"
|
||||
v-model="item[name]"
|
||||
/>
|
||||
<input
|
||||
v-else-if="name === 'ignore_lines'"
|
||||
type="text"
|
||||
class="form-control"
|
||||
:id="name"
|
||||
v-model="formatIgnoreLines"
|
||||
/>
|
||||
<input v-else type="text" class="form-control" :id="name" v-model="item[name]" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-1" style="min-width: 85px">
|
||||
<button class="btn btn-primary" type="submit" variant="primary">Save</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div
|
||||
id="restartModal"
|
||||
ref="restartModal"
|
||||
class="modal"
|
||||
tabindex="-1"
|
||||
aria-labelledby="restartModalLabel"
|
||||
aria-hidden="true"
|
||||
<div class="max-w-[1200px] pe-8">
|
||||
<h2 class="pt-3 text-3xl">Playout Configuration</h2>
|
||||
<form
|
||||
v-if="configStore.configPlayout"
|
||||
@submit.prevent="onSubmitPlayout"
|
||||
class="mt-10 grid md:grid-cols-[140px_auto] gap-4"
|
||||
>
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h1 class="modal-title fs-5" id="restartModalLabel">Restart Playout</h1>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Cancel"></button>
|
||||
</div>
|
||||
<div class="modal-body">Restart ffplayout to apply changes?</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-primary" data-bs-dismiss="modal">No</button>
|
||||
<button type="button" class="btn btn-primary" data-bs-dismiss="modal" @click="restart()">
|
||||
Yes
|
||||
</button>
|
||||
</div>
|
||||
<template v-for="(item, key) in configStore.configPlayout" class="" :key="key">
|
||||
<div class="text-xl">{{ key }}:</div>
|
||||
<div class="md:pt-10">
|
||||
<label
|
||||
v-for="(prop, name) in (item as Record<string, any>)"
|
||||
class="form-control w-full mt-4"
|
||||
:class="typeof prop === 'boolean' && 'flex-row'"
|
||||
>
|
||||
<div class="label">
|
||||
<span class="label-text !text-md font-bold">{{ name }}</span>
|
||||
</div>
|
||||
<div v-if="name.toString() === 'help_text'">{{ prop }}</div>
|
||||
<input
|
||||
v-else-if="name.toString() === 'sender_pass'"
|
||||
type="password"
|
||||
placeholder="Password"
|
||||
class="input input-sm input-bordered w-full"
|
||||
v-model="item[name]"
|
||||
/>
|
||||
<textarea
|
||||
v-else-if="name.toString() === 'output_param' || name.toString() === 'custom_filter'"
|
||||
class="textarea textarea-bordered"
|
||||
v-model="item[name]"
|
||||
rows="3"
|
||||
/>
|
||||
<input
|
||||
v-else-if="typeof prop === 'number' && prop % 1 === 0"
|
||||
type="number"
|
||||
class="input input-sm input-bordered w-full"
|
||||
v-model="item[name]"
|
||||
/>
|
||||
<input
|
||||
v-else-if="typeof prop === 'number'"
|
||||
type="number"
|
||||
class="input input-sm input-bordered w-full"
|
||||
v-model="item[name]"
|
||||
step="0.0001"
|
||||
style="max-width: 250px"
|
||||
/>
|
||||
<input
|
||||
v-else-if="typeof prop === 'boolean'"
|
||||
type="checkbox"
|
||||
class="checkbox checkbox-sm ms-2 mt-2"
|
||||
v-model="item[name]"
|
||||
/>
|
||||
<input
|
||||
v-else-if="name === 'ignore_lines'"
|
||||
type="text"
|
||||
class="input input-sm input-bordered w-full"
|
||||
v-model="formatIgnoreLines"
|
||||
/>
|
||||
<input
|
||||
v-else
|
||||
type="text"
|
||||
class="input input-sm input-bordered w-full"
|
||||
:id="name"
|
||||
v-model="item[name]"
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
</template>
|
||||
<div class="mt-5 mb-10">
|
||||
<button class="btn btn-primary" type="submit">Save</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<Modal title="Restart Playout" text="Restart ffplayout to apply changes?" :show="showModal" :modalAction="restart" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const { $bootstrap } = useNuxtApp()
|
||||
|
||||
const authStore = useAuth()
|
||||
const configStore = useConfig()
|
||||
const indexStore = useIndex()
|
||||
|
||||
const contentType = { 'content-type': 'application/json;charset=UTF-8' }
|
||||
|
||||
const restartModal = ref()
|
||||
const showModal = ref(false)
|
||||
|
||||
const formatIgnoreLines = computed({
|
||||
get() {
|
||||
@ -117,7 +92,7 @@ const formatIgnoreLines = computed({
|
||||
|
||||
set(value) {
|
||||
configStore.configPlayout.logging.ignore_lines = value.split(';')
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
async function onSubmitPlayout() {
|
||||
@ -134,24 +109,25 @@ async function onSubmitPlayout() {
|
||||
body: JSON.stringify({ command: 'status' }),
|
||||
}).then((response: any) => {
|
||||
if (response === 'active') {
|
||||
// @ts-ignore
|
||||
const modal = $bootstrap.Modal.getOrCreateInstance(restartModal.value)
|
||||
|
||||
modal.show()
|
||||
showModal.value = true
|
||||
}
|
||||
})
|
||||
} else {
|
||||
indexStore.msgAlert('alert-danger', 'Update playout config failed!', 2)
|
||||
indexStore.msgAlert('alert-error', 'Update playout config failed!', 2)
|
||||
}
|
||||
}
|
||||
|
||||
async function restart() {
|
||||
const channel = configStore.configGui[configStore.configID].id
|
||||
async function restart(res: boolean) {
|
||||
if (res) {
|
||||
const channel = configStore.configGui[configStore.configID].id
|
||||
|
||||
await $fetch(`/api/control/${channel}/process/`, {
|
||||
method: 'POST',
|
||||
headers: { ...contentType, ...authStore.authHeader },
|
||||
body: JSON.stringify({ command: 'restart' }),
|
||||
})
|
||||
await $fetch(`/api/control/${channel}/process/`, {
|
||||
method: 'POST',
|
||||
headers: { ...contentType, ...authStore.authHeader },
|
||||
body: JSON.stringify({ command: 'restart' }),
|
||||
})
|
||||
}
|
||||
|
||||
showModal.value = false
|
||||
}
|
||||
</script>
|
||||
|
@ -1,137 +1,123 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="container">
|
||||
<h2 class="pb-4 pt-3">User Configuration</h2>
|
||||
<div class="row w-100" style="height: 43px">
|
||||
<div class="col-sm-2"></div>
|
||||
<div class="col-sm-4 ms-1">
|
||||
<select class="form-select" v-model="selected" @change="onChange($event)">
|
||||
<option v-for="item in users">{{ item.username }}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col px-0">
|
||||
<div class="float-end">
|
||||
<button
|
||||
class="btn btn-primary me-2"
|
||||
title="Add new User"
|
||||
data-bs-toggle="modal"
|
||||
data-bs-target="#userModal"
|
||||
>
|
||||
Add User
|
||||
</button>
|
||||
<button class="btn btn-primary" @click="deleteUser()">Delete</button>
|
||||
</div>
|
||||
<div class="max-w-[1200px] pe-8">
|
||||
<h2 class="pt-3 text-3xl">User Configuration</h2>
|
||||
<div class="flex w-full mt-5">
|
||||
<div class="col-sm-2"></div>
|
||||
<div class="col-sm-4 ms-1">
|
||||
<select class="form-select" v-model="selected" @change="onChange($event)">
|
||||
<option v-for="item in users">{{ item.username }}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col px-0">
|
||||
<div class="float-end">
|
||||
<button
|
||||
class="btn btn-primary me-2"
|
||||
title="Add new User"
|
||||
data-bs-toggle="modal"
|
||||
data-bs-target="#userModal"
|
||||
>
|
||||
Add User
|
||||
</button>
|
||||
<button class="btn btn-primary" @click="deleteUser()">Delete</button>
|
||||
</div>
|
||||
</div>
|
||||
<form v-if="configStore.configUser" @submit.prevent="onSubmitUser">
|
||||
<div class="mb-3 row">
|
||||
<label for="userName" class="col-sm-2 col-form-label">Username</label>
|
||||
<div class="col-sm-10">
|
||||
</div>
|
||||
<form v-if="configStore.configUser" @submit.prevent="onSubmitUser">
|
||||
<div class="mb-3 row">
|
||||
<label for="userName" class="col-sm-2 col-form-label">Username</label>
|
||||
<div class="col-sm-10">
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
id="userName"
|
||||
v-model="configStore.configUser.username"
|
||||
disabled
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3 row">
|
||||
<label for="userMail" class="col-sm-2 col-form-label">mail</label>
|
||||
<div class="col-sm-10">
|
||||
<input type="email" class="form-control" id="userMail" v-model="configStore.configUser.mail" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3 row">
|
||||
<label for="userPass1" class="col-sm-2 col-form-label">New Password</label>
|
||||
<div class="col-sm-10">
|
||||
<input type="password" class="form-control" id="userPass1" v-model="newPass" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3 row">
|
||||
<label for="userPass2" class="col-sm-2 col-form-label">Confirm Password</label>
|
||||
<div class="col-sm-10">
|
||||
<input type="password" class="form-control" id="userPass2" v-model="confirmPass" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-1" style="min-width: 85px">
|
||||
<button class="btn btn-primary" type="submit">Save</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div id="userModal" ref="userModal" class="modal" tabindex="-1" aria-labelledby="userModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h1 class="modal-title fs-5" id="userModalLabel">Add User</h1>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Cancel"></button>
|
||||
</div>
|
||||
<form @reset="clearUser" @submit.prevent="addUser">
|
||||
<div class="modal-body">
|
||||
<label for="name-input" class="form-label">Username</label>
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
id="userName"
|
||||
v-model="configStore.configUser.username"
|
||||
disabled
|
||||
id="name-input"
|
||||
aria-describedby="Username"
|
||||
v-model.number="user.username"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3 row">
|
||||
<label for="userMail" class="col-sm-2 col-form-label">mail</label>
|
||||
<div class="col-sm-10">
|
||||
<input type="email" class="form-control" id="userMail" v-model="configStore.configUser.mail" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3 row">
|
||||
<label for="userPass1" class="col-sm-2 col-form-label">New Password</label>
|
||||
<div class="col-sm-10">
|
||||
<input type="password" class="form-control" id="userPass1" v-model="newPass" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3 row">
|
||||
<label for="userPass2" class="col-sm-2 col-form-label">Confirm Password</label>
|
||||
<div class="col-sm-10">
|
||||
<input type="password" class="form-control" id="userPass2" v-model="confirmPass" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-1" style="min-width: 85px">
|
||||
<button class="btn btn-primary" type="submit">Save</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div
|
||||
id="userModal"
|
||||
ref="userModal"
|
||||
class="modal"
|
||||
tabindex="-1"
|
||||
aria-labelledby="userModalLabel"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h1 class="modal-title fs-5" id="userModalLabel">Add User</h1>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Cancel"></button>
|
||||
</div>
|
||||
<form @reset="clearUser" @submit.prevent="addUser">
|
||||
<div class="modal-body">
|
||||
<label for="name-input" class="form-label">Username</label>
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
id="name-input"
|
||||
aria-describedby="Username"
|
||||
v-model.number="user.username"
|
||||
required
|
||||
/>
|
||||
<label for="mail-input" class="form-label mt-2">Mail</label>
|
||||
<input
|
||||
type="email"
|
||||
class="form-control"
|
||||
id="mail-input"
|
||||
aria-describedby="Mail Address"
|
||||
v-model.number="user.mail"
|
||||
required
|
||||
/>
|
||||
<label for="pass-input" class="form-label mt-2">Password</label>
|
||||
<input
|
||||
type="password"
|
||||
class="form-control"
|
||||
id="pass-input"
|
||||
aria-describedby="Password"
|
||||
v-model.string="user.password"
|
||||
required
|
||||
/>
|
||||
<label for="confirm-input" class="form-label mt-2">Confirm Password</label>
|
||||
<input
|
||||
type="password"
|
||||
class="form-control"
|
||||
id="confirm-input"
|
||||
aria-describedby="Confirm Password"
|
||||
v-model.string="user.confirm"
|
||||
required
|
||||
/>
|
||||
<div class="form-check mt-3">
|
||||
<input
|
||||
class="form-check-input"
|
||||
type="checkbox"
|
||||
id="isAdmin"
|
||||
v-model.number="user.admin"
|
||||
/>
|
||||
<label class="form-check-label" for="isAdmin">Admin</label>
|
||||
</div>
|
||||
<label for="mail-input" class="form-label mt-2">Mail</label>
|
||||
<input
|
||||
type="email"
|
||||
class="form-control"
|
||||
id="mail-input"
|
||||
aria-describedby="Mail Address"
|
||||
v-model.number="user.mail"
|
||||
required
|
||||
/>
|
||||
<label for="pass-input" class="form-label mt-2">Password</label>
|
||||
<input
|
||||
type="password"
|
||||
class="form-control"
|
||||
id="pass-input"
|
||||
aria-describedby="Password"
|
||||
v-model.string="user.password"
|
||||
required
|
||||
/>
|
||||
<label for="confirm-input" class="form-label mt-2">Confirm Password</label>
|
||||
<input
|
||||
type="password"
|
||||
class="form-control"
|
||||
id="confirm-input"
|
||||
aria-describedby="Confirm Password"
|
||||
v-model.string="user.confirm"
|
||||
required
|
||||
/>
|
||||
<div class="form-check mt-3">
|
||||
<input class="form-check-input" type="checkbox" id="isAdmin" v-model.number="user.admin" />
|
||||
<label class="form-check-label" for="isAdmin">Admin</label>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="reset" class="btn btn-primary" data-bs-dismiss="modal" aria-label="Cancel">
|
||||
Cancel
|
||||
</button>
|
||||
<button type="submit" class="btn btn-primary">Ok</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="reset" class="btn btn-primary" data-bs-dismiss="modal" aria-label="Cancel">
|
||||
Cancel
|
||||
</button>
|
||||
<button type="submit" class="btn btn-primary">Ok</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -200,20 +186,21 @@ async function getUserConfig() {
|
||||
|
||||
async function deleteUser() {
|
||||
if (configStore.configUser.username === configStore.currentUser) {
|
||||
indexStore.msgAlert('alert-danger', 'Delete current user not possible!', 2)
|
||||
indexStore.msgAlert('alert-error', 'Delete current user not possible!', 2)
|
||||
} else {
|
||||
await fetch(`/api/user/${configStore.configUser.username}`, {
|
||||
method: 'DELETE',
|
||||
headers: authStore.authHeader,
|
||||
}).then(async () => {
|
||||
indexStore.msgAlert('alert-success', 'Delete user done!', 2)
|
||||
})
|
||||
.then(async () => {
|
||||
indexStore.msgAlert('alert-success', 'Delete user done!', 2)
|
||||
|
||||
await configStore.getUserConfig()
|
||||
await getUsers()
|
||||
})
|
||||
.catch((e) => {
|
||||
indexStore.msgAlert('alert-danger', `Delete user error: ${e}`, 2)
|
||||
})
|
||||
await configStore.getUserConfig()
|
||||
await getUsers()
|
||||
})
|
||||
.catch((e) => {
|
||||
indexStore.msgAlert('alert-error', `Delete user error: ${e}`, 2)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -249,12 +236,12 @@ async function addUser() {
|
||||
await getUsers()
|
||||
await getUserConfig()
|
||||
} else {
|
||||
indexStore.msgAlert('alert-danger', 'Add user failed!', 2)
|
||||
indexStore.msgAlert('alert-error', 'Add user failed!', 2)
|
||||
}
|
||||
|
||||
clearUser()
|
||||
} else {
|
||||
indexStore.msgAlert('alert-danger', 'Password mismatch!', 2)
|
||||
indexStore.msgAlert('alert-error', 'Password mismatch!', 2)
|
||||
}
|
||||
}
|
||||
|
||||
@ -269,7 +256,7 @@ async function onSubmitUser() {
|
||||
if (update.status === 200) {
|
||||
indexStore.msgAlert('alert-success', 'Update user profile success!', 2)
|
||||
} else {
|
||||
indexStore.msgAlert('alert-danger', 'Update user profile failed!', 2)
|
||||
indexStore.msgAlert('alert-error', 'Update user profile failed!', 2)
|
||||
}
|
||||
|
||||
newPass.value = ''
|
||||
|
@ -1,72 +1,87 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="menu">
|
||||
<nav class="navbar navbar-expand-sm fixed-top custom-nav">
|
||||
<div class="container-fluid">
|
||||
<NuxtLink class="navbar-brand p-2" href="/"
|
||||
><img
|
||||
src="~/assets/images/ffplayout-small.png"
|
||||
class="img-fluid"
|
||||
alt="Logo"
|
||||
width="30"
|
||||
height="30"
|
||||
/></NuxtLink>
|
||||
<button
|
||||
class="navbar-toggler"
|
||||
type="button"
|
||||
data-bs-toggle="collapse"
|
||||
data-bs-target="#navbarNavDropdown"
|
||||
aria-controls="navbarNavDropdown"
|
||||
aria-expanded="false"
|
||||
aria-label="Toggle navigation"
|
||||
<div class="navbar bg-base-100 min-h-[52px] p-0">
|
||||
<NuxtLink class="navbar-brand p-2" href="/">
|
||||
<img src="~/assets/images/ffplayout-small.png" class="img-fluid" alt="Logo" width="30" height="30" />
|
||||
</NuxtLink>
|
||||
<div class="navbar-end w-1/5 grow">
|
||||
<div class="dropdown dropdown-end z-50">
|
||||
<div tabindex="0" role="button" class="btn btn-ghost md:hidden">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="h-5 w-5"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
>
|
||||
<span class="navbar-toggler-icon"> </span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse justify-content-end" id="navbarNavDropdown">
|
||||
<ul class="navbar-nav">
|
||||
<li class="nav-item">
|
||||
<NuxtLink class="btn btn-primary btn-sm" to="/">Home</NuxtLink>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<NuxtLink class="btn btn-primary btn-sm" to="/player">Player</NuxtLink>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<NuxtLink class="btn btn-primary btn-sm" to="/media">Media</NuxtLink>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<NuxtLink class="btn btn-primary btn-sm" to="/message">Message</NuxtLink>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<NuxtLink class="btn btn-primary btn-sm" to="/logging">Logging</NuxtLink>
|
||||
</li>
|
||||
<li v-if="authStore.role.toLowerCase() == 'admin'" class="nav-item">
|
||||
<NuxtLink class="btn btn-primary btn-sm" to="/configure">Configure</NuxtLink>
|
||||
</li>
|
||||
<li v-if="configStore.configGui.length > 1" class="nav-item dropdown">
|
||||
|
||||
<a
|
||||
class="btn btn-primary btn-sm dropdown-toggle"
|
||||
href="#"
|
||||
role="button"
|
||||
data-bs-toggle="dropdown"
|
||||
aria-expanded="false"
|
||||
>
|
||||
{{ configStore.configGui[configStore.configID].name }}
|
||||
</a>
|
||||
<ul class="dropdown-menu dropdown-menu-dark dropdown-menu-end">
|
||||
<li v-for="(channel, index) in configStore.configGui" :key="index">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M4 6h16M4 12h8m-8 6h16"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<ul class="menu menu-sm dropdown-content mt-1 z-[1] p-2 shadow bg-base-100 rounded-box w-52">
|
||||
<li v-for="item in menuItems" :key="item.name" class="bg-base-100 rounded-md">
|
||||
<details v-if="item.name === 'channel' && configStore.configGui.length > 1">
|
||||
<summary>
|
||||
<NuxtLink :to="item.link" class="h-[19px] text-base">
|
||||
<span>
|
||||
{{ item.name }}
|
||||
</span>
|
||||
</NuxtLink>
|
||||
</summary>
|
||||
<ul class="p-2">
|
||||
<li v-for="(channel, index) in configStore.configGui" :key="index">
|
||||
<span>
|
||||
<a class="dropdown-item" @click="selectChannel(index)">{{ channel.name }}</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="btn btn-primary btn-sm" @click="logout()">Logout</a>
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</details>
|
||||
<NuxtLink v-else :to="item.link" class="h-[27px] text-base" exactActiveClass="is-active">
|
||||
<span>
|
||||
{{ item.name }}
|
||||
</span>
|
||||
</NuxtLink>
|
||||
</li>
|
||||
<li class="bg-base-100 rounded-md">
|
||||
<button class="h-[27px]" exactActiveClass="is-active" @click="logout()">Logout</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="navbar-end hidden md:flex w-4/5 min-w-[600px]">
|
||||
<ul class="menu menu-sm menu-horizontal px-1">
|
||||
<li v-for="item in menuItems" :key="item.name" class="bg-base-100 rounded-md p-0">
|
||||
<details
|
||||
v-if="item.name === 'channel' && configStore.configGui.length > 1"
|
||||
tabindex="0"
|
||||
@focusout="closeDropdown"
|
||||
>
|
||||
<summary>
|
||||
<div class="h-[19px] text-base" activeClass="is-active">
|
||||
<span>
|
||||
{{ item.name }}
|
||||
</span>
|
||||
</div>
|
||||
</summary>
|
||||
<ul class="p-2 bg-base-100 rounded-md !mt-1 w-36" tabindex="0">
|
||||
<li v-for="(channel, index) in configStore.configGui" :key="index">
|
||||
<a class="dropdown-item" @click="selectChannel(index)">{{ channel.name }}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</details>
|
||||
<NuxtLink v-else :to="item.link" class="px-2 h-[27px] relative text-base" activeClass="is-active">
|
||||
<span>
|
||||
{{ item.name }}
|
||||
</span>
|
||||
</NuxtLink>
|
||||
</li>
|
||||
<li class="bg-base-100 rounded-md p-0">
|
||||
<button class="h-[27px] pt-[6px]" @click="logout()">Logout</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@ -76,6 +91,22 @@ const authStore = useAuth()
|
||||
const configStore = useConfig()
|
||||
const router = useRouter()
|
||||
|
||||
const menuItems = ref([
|
||||
{ name: 'Home', link: '/' },
|
||||
{ name: 'Player', link: '/player' },
|
||||
{ name: 'Media', link: '/media' },
|
||||
{ name: 'Message', link: '/message' },
|
||||
{ name: 'Logging', link: '/logging' },
|
||||
{ name: 'channel', link: '#' },
|
||||
{ name: 'Configure', link: '/configure' },
|
||||
])
|
||||
|
||||
function closeDropdown($event: any) {
|
||||
setTimeout(() => {
|
||||
$event.target.parentNode.removeAttribute('open')
|
||||
}, 200)
|
||||
}
|
||||
|
||||
function logout() {
|
||||
authStore.removeToken()
|
||||
router.push({ path: '/' })
|
||||
@ -86,43 +117,16 @@ function selectChannel(index: number) {
|
||||
configStore.getPlayoutConfig()
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.menu {
|
||||
width: 100%;
|
||||
height: 60px;
|
||||
margin: 0;
|
||||
|
||||
div {
|
||||
padding: 0.3em;
|
||||
}
|
||||
}
|
||||
|
||||
.custom-nav {
|
||||
background-color: $bg-primary;
|
||||
}
|
||||
|
||||
.nav-item .btn {
|
||||
<style lang="scss" scoped>
|
||||
.is-active > span::after {
|
||||
background: var(--my-accent);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.router-link-exact-active::after {
|
||||
background: $accent;
|
||||
left: 0px;
|
||||
content: ' ';
|
||||
width: 100%;
|
||||
width: inherit;
|
||||
height: 2px;
|
||||
position: absolute;
|
||||
color: red;
|
||||
display: block;
|
||||
left: 0;
|
||||
right: 0;
|
||||
border-radius: 1px;
|
||||
}
|
||||
|
||||
@media (max-width: 575px) {
|
||||
.nav-item .btn {
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
margin-bottom: .3em;
|
||||
}
|
||||
border-radius: 0.15em;
|
||||
}
|
||||
</style>
|
||||
|
41
components/Modal.vue
Normal file
41
components/Modal.vue
Normal file
@ -0,0 +1,41 @@
|
||||
<template>
|
||||
<div v-if="show" class="z-50 fixed top-0 bottom-0 left-0 right-0 flex justify-center items-center bg-black/30">
|
||||
<div class="flex flex-col bg-base-100 w-[400px] h-[200px] mt-[10%] rounded-md p-5 shadow-xl">
|
||||
<div class="font-bold text-lg">{{ title }}</div>
|
||||
<div class="grow mt-3" v-html="text" />
|
||||
|
||||
<div class="flex justify-end">
|
||||
<div class="join">
|
||||
<button class="btn btn-sm bg-base-300 hover:bg-base-300/50 join-item" @click="modalAction(false)">
|
||||
Cancel
|
||||
</button>
|
||||
<button class="btn btn-sm bg-base-300 hover:bg-base-300/50 join-item" @click="modalAction(true)">
|
||||
Ok
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
defineProps({
|
||||
title: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
text: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
modalAction: {
|
||||
type: Function,
|
||||
default() {
|
||||
return ''
|
||||
},
|
||||
},
|
||||
show: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
})
|
||||
</script>
|
@ -1,59 +1,61 @@
|
||||
<template>
|
||||
<div class="row sys-container text-start bg-secondary border border-3 rounded">
|
||||
<div class="col-6 bg-primary p-2 border-top border-start border-end fs-2">
|
||||
<div class="grid grid-cols-1 xs:grid-cols-2 border-4 rounded-md border-primary text-left">
|
||||
<div class="text-3xl flex items-center p-4 bg-base-100">
|
||||
{{ sysStat.system.name }} {{ sysStat.system.version }}
|
||||
</div>
|
||||
<div class="col-6 p-2 border">
|
||||
<div class="fs-4">CPU</div>
|
||||
<div class="row">
|
||||
<div class="col"><strong>Cores:</strong> {{ sysStat.cpu.cores }}</div>
|
||||
<div class="col"><strong>Usage:</strong> {{ sysStat.cpu.usage.toFixed(2) }}%</div>
|
||||
<div class="p-4 border border-primary">
|
||||
<div class="text-xl">CPU</div>
|
||||
<div class="grid grid-cols-2">
|
||||
<div><strong>Cores:</strong> {{ sysStat.cpu.cores }}</div>
|
||||
<div><strong>Usage:</strong> {{ sysStat.cpu.usage.toFixed(2) }}%</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="sysStat.system.kernel" class="col-6 bg-primary border-start border-end border-bottom">
|
||||
<div v-if="sysStat.system.kernel" class="p-4 bg-base-100">
|
||||
{{ sysStat.system.kernel }} <br />
|
||||
<span v-if="sysStat.system.ffp_version"><strong>ffplayout version:</strong> {{ sysStat.system.ffp_version }}</span>
|
||||
<span v-if="sysStat.system.ffp_version"
|
||||
><strong>ffplayout version:</strong> {{ sysStat.system.ffp_version }}</span
|
||||
>
|
||||
</div>
|
||||
<div class="col-6 p-2 border">
|
||||
<div class="fs-4">Load</div>
|
||||
<div class="row">
|
||||
<div class="col">{{ sysStat.load.one }}</div>
|
||||
<div class="col">{{ sysStat.load.five }}</div>
|
||||
<div class="col">{{ sysStat.load.fifteen }}</div>
|
||||
<div class="p-4 border border-primary">
|
||||
<div class="text-xl">Load</div>
|
||||
<div class="grid grid-cols-3">
|
||||
<div>{{ sysStat.load.one }}</div>
|
||||
<div>{{ sysStat.load.five }}</div>
|
||||
<div>{{ sysStat.load.fifteen }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6 border">
|
||||
<div class="fs-4">Memory</div>
|
||||
<div class="row">
|
||||
<div class="col"><strong>Total:</strong> {{ fileSize(sysStat.memory.total) }}</div>
|
||||
<div class="col"><strong>Usage:</strong> {{ fileSize(sysStat.memory.used) }}</div>
|
||||
<div class="p-4 border border-primary">
|
||||
<div class="text-xl">Memory</div>
|
||||
<div class="grid grid-cols-2">
|
||||
<div><strong>Total:</strong> {{ fileSize(sysStat.memory.total) }}</div>
|
||||
<div><strong>Usage:</strong> {{ fileSize(sysStat.memory.used) }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6 p-2 border">
|
||||
<div class="fs-4">Swap</div>
|
||||
<div class="row">
|
||||
<div class="col"><strong>Total:</strong> {{ fileSize(sysStat.swap.total) }}</div>
|
||||
<div class="col"><strong>Usage:</strong> {{ fileSize(sysStat.swap.used) }}</div>
|
||||
<div class="p-4 border border-primary">
|
||||
<div class="text-xl">Swap</div>
|
||||
<div class="grid grid-cols-2">
|
||||
<div><strong>Total:</strong> {{ fileSize(sysStat.swap.total) }}</div>
|
||||
<div><strong>Usage:</strong> {{ fileSize(sysStat.swap.used) }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6 p-2 border">
|
||||
<div class="fs-4">
|
||||
<div class="p-4 border border-primary">
|
||||
<div class="text-xl">
|
||||
Network <span v-if="sysStat.network" class="fs-6">{{ sysStat.network?.name }}</span>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-6"><strong>In:</strong> {{ fileSize(sysStat.network?.current_in) }}</div>
|
||||
<div class="col-6"><strong>Out:</strong> {{ fileSize(sysStat.network?.current_out) }}</div>
|
||||
<div class="col-6">{{ fileSize(sysStat.network?.total_in) }}</div>
|
||||
<div class="col-6">{{ fileSize(sysStat.network?.total_out) }}</div>
|
||||
<div class="grid grid-cols-2">
|
||||
<div><strong>In:</strong> {{ fileSize(sysStat.network?.current_in) }}</div>
|
||||
<div><strong>Out:</strong> {{ fileSize(sysStat.network?.current_out) }}</div>
|
||||
<div>{{ fileSize(sysStat.network?.total_in) }}</div>
|
||||
<div>{{ fileSize(sysStat.network?.total_out) }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="sysStat.storage?.path" class="col-6 p-2 border">
|
||||
<div class="fs-4">Storage</div>
|
||||
<div v-if="sysStat.storage?.path" class="p-4 border border-primary">
|
||||
<div class="text-xl">Storage</div>
|
||||
<div v-if="sysStat.storage"><strong>Device:</strong> {{ sysStat.storage?.path }}</div>
|
||||
|
||||
<div class="row" v-if="sysStat.storage">
|
||||
<div class="col"><strong>Size:</strong> {{ fileSize(sysStat.storage?.total) }}</div>
|
||||
<div class="col"><strong>Used:</strong> {{ fileSize(sysStat.storage?.used) }}</div>
|
||||
<div class="grid grid-cols-2" v-if="sysStat.storage">
|
||||
<div><strong>Size:</strong> {{ fileSize(sysStat.storage?.total) }}</div>
|
||||
<div><strong>Used:</strong> {{ fileSize(sysStat.storage?.used) }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="col-6 bg-primary p-2 border" />
|
||||
@ -87,7 +89,7 @@ onBeforeUnmount(() => {
|
||||
})
|
||||
|
||||
async function status() {
|
||||
systemStatus()
|
||||
systemStatus()
|
||||
|
||||
async function setStatus(resolve: any) {
|
||||
/*
|
||||
@ -104,18 +106,12 @@ async function systemStatus() {
|
||||
const channel = configStore.configGui[configStore.configID].id
|
||||
|
||||
if (!document?.hidden) {
|
||||
await $fetch<SystemStatistics>(`/api/system/${channel}`, {
|
||||
await $fetch(`/api/system/${channel}`, {
|
||||
method: 'GET',
|
||||
headers: { ...contentType, ...authStore.authHeader },
|
||||
}).then((stat) => {
|
||||
}).then((stat: SystemStatistics) => {
|
||||
sysStat.value = stat
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
.sys-container {
|
||||
max-width: 640px;
|
||||
min-height: 300px;
|
||||
}
|
||||
</style>
|
||||
|
@ -1,37 +1,31 @@
|
||||
<template>
|
||||
<main>
|
||||
<slot />
|
||||
<div
|
||||
v-if="indexStore.showAlert"
|
||||
class="alert show alert-dismissible fade media-alert position-fixed"
|
||||
:class="indexStore.alertVariant"
|
||||
role="alert"
|
||||
>
|
||||
{{ indexStore.alertMsg }}
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close" @click="indexStore.showAlert=false"></button>
|
||||
<div class="min-h-screen bg-base-200">
|
||||
<div v-if="authStore.isLogin && route.name !== 'index'">
|
||||
<Menu />
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<main class="h-[calc(100%-52px)]">
|
||||
<slot />
|
||||
</main>
|
||||
|
||||
<Alert />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const { $bootstrap } = useNuxtApp()
|
||||
const configStore = useConfig()
|
||||
const indexStore = useIndex()
|
||||
const authStore = useAuth()
|
||||
|
||||
useHead({
|
||||
htmlAttrs: {
|
||||
lang: 'en',
|
||||
"data-bs-theme": "dark"
|
||||
}
|
||||
})
|
||||
const route = useRoute()
|
||||
|
||||
console.log(route.name)
|
||||
|
||||
onMounted(() => {
|
||||
// @ts-ignore
|
||||
new $bootstrap.Tooltip(document.body, {
|
||||
selector: "[data-tooltip=tooltip]",
|
||||
container: "body"
|
||||
})
|
||||
// new $bootstrap.Tooltip(document.body, {
|
||||
// selector: "[data-tooltip=tooltip]",
|
||||
// container: "body"
|
||||
// })
|
||||
})
|
||||
await configStore.nuxtClientInit()
|
||||
</script>
|
||||
|
||||
|
@ -45,9 +45,21 @@ export default defineNuxtConfig({
|
||||
},
|
||||
},
|
||||
|
||||
modules: ['@pinia/nuxt', '@vueuse/nuxt'],
|
||||
modules: ['@nuxtjs/color-mode', '@pinia/nuxt', '@vueuse/nuxt', '@nuxtjs/tailwindcss'],
|
||||
css: ['@/assets/scss/main.scss'],
|
||||
|
||||
colorMode: {
|
||||
preference: 'dark', // default value of $colorMode.preference
|
||||
fallback: 'system', // fallback value if not system preference found
|
||||
hid: 'nuxt-color-mode-script',
|
||||
globalName: '__NUXT_COLOR_MODE__',
|
||||
componentName: 'ColorScheme',
|
||||
classPrefix: '',
|
||||
classSuffix: '',
|
||||
dataValue: 'theme',
|
||||
storageKey: 'theme',
|
||||
},
|
||||
|
||||
vite: {
|
||||
css: {
|
||||
preprocessorOptions: {
|
||||
|
1414
package-lock.json
generated
1414
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -14,10 +14,10 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@nuxt/types": "^2.17.3",
|
||||
"@nuxtjs/color-mode": "^3.3.3",
|
||||
"@pinia/nuxt": "^0.5.1",
|
||||
"@popperjs/core": "^2.11.8",
|
||||
"@vueuse/nuxt": "^10.9.0",
|
||||
"bootstrap": "^5.3.3",
|
||||
"bootstrap-icons": "^1.11.3",
|
||||
"cookie-universal-nuxt": "^2.2.2",
|
||||
"dayjs": "^1.11.10",
|
||||
@ -34,10 +34,11 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@nuxtjs/eslint-config": "^12.0.0",
|
||||
"@types/bootstrap": "^5.2.10",
|
||||
"@nuxtjs/tailwindcss": "^6.11.4",
|
||||
"@types/lodash": "^4.14.202",
|
||||
"@types/splitpanes": "^2.2.6",
|
||||
"@types/video.js": "^7.3.56",
|
||||
"daisyui": "^4.9.0",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-plugin-nuxt": "^4.0.0",
|
||||
"postcss": "^8.4.35",
|
||||
|
@ -1,68 +1,39 @@
|
||||
<template>
|
||||
<div>
|
||||
<Menu />
|
||||
<div class="container-fluid configure-container">
|
||||
<div class="d-flex align-items-start h-100">
|
||||
<div class="nav flex-column nav-pills h-100 me-3" id="v-pills-tab" role="tablist" aria-orientation="vertical">
|
||||
<button
|
||||
class="nav-link active"
|
||||
id="v-pills-gui-tab"
|
||||
data-bs-toggle="pill"
|
||||
data-bs-target="#v-pills-gui"
|
||||
type="button"
|
||||
role="tab"
|
||||
aria-controls="v-pills-gui"
|
||||
aria-selected="true"
|
||||
>
|
||||
GUI
|
||||
</button>
|
||||
<button
|
||||
class="nav-link"
|
||||
id="v-pills-playout-tab"
|
||||
data-bs-toggle="pill"
|
||||
data-bs-target="#v-pills-playout"
|
||||
type="button"
|
||||
role="tab"
|
||||
aria-controls="v-pills-playout"
|
||||
aria-selected="false"
|
||||
>
|
||||
Playout
|
||||
</button>
|
||||
<button
|
||||
class="nav-link"
|
||||
id="v-pills-user-tab"
|
||||
data-bs-toggle="pill"
|
||||
data-bs-target="#v-pills-user"
|
||||
type="button"
|
||||
role="tab"
|
||||
aria-controls="v-pills-user"
|
||||
aria-selected="false"
|
||||
>
|
||||
User
|
||||
</button>
|
||||
</div>
|
||||
<div class="tab-content h-100" id="v-pills-tabContent">
|
||||
<div
|
||||
class="tab-pane h-100 overflow-y-auto show active"
|
||||
id="v-pills-gui"
|
||||
role="tabpanel"
|
||||
aria-labelledby="v-pills-gui-tab"
|
||||
>
|
||||
<div class="config-container">
|
||||
<ConfigGui />
|
||||
</div>
|
||||
</div>
|
||||
<div class="tab-pane h-100 overflow-y-auto" id="v-pills-playout" role="tabpanel" aria-labelledby="v-pills-playout-tab">
|
||||
<div class="config-container">
|
||||
<ConfigPlayout />
|
||||
</div>
|
||||
</div>
|
||||
<div class="tab-pane h-100 overflow-y-auto" id="v-pills-user" role="tabpanel" aria-labelledby="v-pills-user-tab">
|
||||
<div class="config-container">
|
||||
<ConfigUser />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex w-full h-full">
|
||||
<div class="flex-none w-[70px] me-3 px-1 pt-7">
|
||||
<button
|
||||
class="w-full btn btn-sm btn-primary duration-500"
|
||||
:class="activeConf === 1 && 'btn-secondary'"
|
||||
@click="activeConf = 1"
|
||||
>
|
||||
GUI
|
||||
</button>
|
||||
<button
|
||||
class="w-full btn btn-sm btn-primary mt-1 duration-500"
|
||||
:class="activeConf === 2 && 'btn-secondary'"
|
||||
@click="activeConf = 2"
|
||||
>
|
||||
Playout
|
||||
</button>
|
||||
<button
|
||||
class="w-full btn btn-sm btn-primary mt-1 duration-500"
|
||||
:class="activeConf === 3 && 'btn-secondary'"
|
||||
@click="activeConf = 3"
|
||||
>
|
||||
User
|
||||
</button>
|
||||
</div>
|
||||
<div class="w-[calc(100%-70px)] mt-10 px-6">
|
||||
<div v-if="activeConf === 1" class="w-full flex justify-center">
|
||||
<ConfigGui />
|
||||
</div>
|
||||
|
||||
<div v-else-if="activeConf === 2" class="w-full flex justify-center">
|
||||
<ConfigPlayout />
|
||||
</div>
|
||||
|
||||
<div v-else-if="activeConf === 3" class="w-full flex justify-center">
|
||||
<ConfigUser />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -70,20 +41,9 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
useHead({
|
||||
title: 'Configuration | ffplayout'
|
||||
title: 'Configuration | ffplayout',
|
||||
})
|
||||
|
||||
const indexStore = useIndex()
|
||||
const activeConf = ref(1)
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.configure-container {
|
||||
height: calc(100% - 90px);
|
||||
}
|
||||
|
||||
.config-container {
|
||||
margin: 2em auto 2em auto;
|
||||
padding: 0;
|
||||
width: 90vw;
|
||||
}
|
||||
</style>
|
||||
|
162
pages/index.vue
162
pages/index.vue
@ -1,75 +1,67 @@
|
||||
<template>
|
||||
<div>
|
||||
<div v-if="authStore.isLogin">
|
||||
<div class="container login-container">
|
||||
<div>
|
||||
<SystemStats v-if="configStore.configGui.length > 0" class="mx-auto" />
|
||||
<div class="text-center mt-5">
|
||||
<div class="btn-group actions-grp btn-group-lg" role="group">
|
||||
<NuxtLink to="/player" class="btn btn-primary">Player</NuxtLink>
|
||||
<NuxtLink to="/media" class="btn btn-primary">Media</NuxtLink>
|
||||
<NuxtLink to="/message" class="btn btn-primary">Message</NuxtLink>
|
||||
<NuxtLink to="logging" class="btn btn-primary">Logging</NuxtLink>
|
||||
<NuxtLink v-if="authStore.role.toLowerCase() == 'admin'" to="/configure" class="btn btn-primary"> Configure </NuxtLink>
|
||||
<button class="btn btn-primary" @click="logout()">Logout</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-full h-screen flex justify-center items-center">
|
||||
<div v-if="authStore.isLogin" class="text-center w-full max-w-[700px] p-5">
|
||||
<SystemStats v-if="configStore.configGui.length > 0" />
|
||||
<div class="flex flex-wrap justify-center gap-1 xs:gap-0 xs:join mt-5">
|
||||
<NuxtLink to="/player" class="btn join-item btn-primary">Player</NuxtLink>
|
||||
<NuxtLink to="/media" class="btn join-item btn-primary">Media</NuxtLink>
|
||||
<NuxtLink to="/message" class="btn join-item btn-primary">Message</NuxtLink>
|
||||
<NuxtLink to="logging" class="btn join-item btn-primary">Logging</NuxtLink>
|
||||
<NuxtLink v-if="authStore.role.toLowerCase() == 'admin'" to="/configure" class="btn join-item btn-primary">
|
||||
Configure
|
||||
</NuxtLink>
|
||||
<button class="btn join-item btn-primary" @click="logout()">Logout</button>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else>
|
||||
<div class="logout-div" />
|
||||
<div class="container login-container">
|
||||
<div>
|
||||
<div class="text-center mb-5">
|
||||
<h1>ffplayout</h1>
|
||||
</div>
|
||||
<div v-else class="w-96 flex flex-col justify-center items-center">
|
||||
<h1 class="text-8xl">ffplayout</h1>
|
||||
|
||||
<form class="login-form" @submit.prevent="login">
|
||||
<div id="input-group-1" class="mb-3">
|
||||
<label for="input-user" class="form-label">User:</label>
|
||||
<input
|
||||
type="text"
|
||||
id="input-user"
|
||||
class="form-control"
|
||||
v-model="formUsername"
|
||||
aria-describedby="Username"
|
||||
required
|
||||
/>
|
||||
|
||||
<form class="mt-10" @submit.prevent="login">
|
||||
<input
|
||||
type="text"
|
||||
v-model="formUsername"
|
||||
placeholder="Username"
|
||||
class="input input-bordered w-full"
|
||||
required
|
||||
/>
|
||||
|
||||
<input
|
||||
type="password"
|
||||
v-model="formPassword"
|
||||
placeholder="Password"
|
||||
class="input input-bordered w-full mt-5"
|
||||
required
|
||||
/>
|
||||
|
||||
<div class="w-full mt-4 grid grid-flow-row-dense grid-cols-12 grid-rows-1 gap-2">
|
||||
<div class="col-span-3">
|
||||
<button type="submit" class="btn btn-primary">Login</button>
|
||||
</div>
|
||||
<div class="col-span-12 sm:col-span-9">
|
||||
<div
|
||||
v-if="showLoginError"
|
||||
role="alert"
|
||||
class="alert alert-error w-auto rounded z-2 h-12 p-[0.7rem]"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="stroke-current shrink-0 h-6 w-6"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z"
|
||||
/>
|
||||
</svg>
|
||||
<span>{{ formError }}</span>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="input-pass" class="form-label">Password:</label>
|
||||
<input
|
||||
type="password"
|
||||
id="input-pass"
|
||||
class="form-control"
|
||||
v-model="formPassword"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-3">
|
||||
<button class="btn btn-primary" type="submit">Login</button>
|
||||
</div>
|
||||
<div class="col-9">
|
||||
<div
|
||||
class="alert alert-danger alert-dismissible fade login-alert"
|
||||
:class="{ show: showError }"
|
||||
role="alert"
|
||||
>
|
||||
{{ formError }}
|
||||
<button
|
||||
type="button"
|
||||
class="btn-close"
|
||||
data-bs-dismiss="alert"
|
||||
aria-label="Close"
|
||||
></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@ -79,7 +71,7 @@ const authStore = useAuth()
|
||||
const configStore = useConfig()
|
||||
|
||||
const formError = ref('')
|
||||
const showError = ref(false)
|
||||
const showLoginError = ref(false)
|
||||
const formUsername = ref('')
|
||||
const formPassword = ref('')
|
||||
|
||||
@ -95,7 +87,11 @@ async function login() {
|
||||
|
||||
if (status === 401 || status === 400 || status === 403) {
|
||||
formError.value = 'Wrong User/Password!'
|
||||
showError.value = true
|
||||
showLoginError.value = true
|
||||
|
||||
setTimeout(() => {
|
||||
showLoginError.value = false
|
||||
}, 3000)
|
||||
}
|
||||
|
||||
await configStore.nuxtClientInit()
|
||||
@ -112,33 +108,3 @@ async function logout() {
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.login-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
.login-form {
|
||||
min-width: 300px;
|
||||
}
|
||||
|
||||
.login-alert {
|
||||
padding: 0.4em;
|
||||
--bs-alert-margin-bottom: 0;
|
||||
|
||||
.btn-close {
|
||||
padding: 0.65rem 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 380px) {
|
||||
.actions-grp {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin: 0 2em 0 2em;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -514,12 +514,12 @@ async function deleteFileOrFolder() {
|
||||
})
|
||||
.then(async (response) => {
|
||||
if (response.status !== 200) {
|
||||
indexStore.msgAlert('alert-danger', `${await response.text()}`, 5)
|
||||
indexStore.msgAlert('alert-error', `${await response.text()}`, 5)
|
||||
}
|
||||
getPath(mediaStore.folderTree.source)
|
||||
})
|
||||
.catch((e) => {
|
||||
indexStore.msgAlert('alert-danger', `Delete error: ${e}`, 5)
|
||||
indexStore.msgAlert('alert-error', `Delete error: ${e}`, 5)
|
||||
})
|
||||
}
|
||||
|
||||
@ -543,7 +543,7 @@ async function onSubmitRenameFile(evt: any) {
|
||||
getPath(mediaStore.folderTree.source)
|
||||
})
|
||||
.catch((e) => {
|
||||
indexStore.msgAlert('alert-danger', `Delete error: ${e}`, 3)
|
||||
indexStore.msgAlert('alert-error', `Delete error: ${e}`, 3)
|
||||
})
|
||||
|
||||
renameOldName.value = ''
|
||||
@ -581,8 +581,8 @@ async function onSubmitCreateFolder(evt: any) {
|
||||
indexStore.msgAlert('alert-success', 'Folder create done...', 2)
|
||||
})
|
||||
.catch((e: string) => {
|
||||
indexStore.msgAlert('alert-danger', `Folder create error: ${e}`, 3)
|
||||
indexStore.alertVariant = 'alert-danger'
|
||||
indexStore.msgAlert('alert-error', `Folder create error: ${e}`, 3)
|
||||
indexStore.alertVariant = 'alert-error'
|
||||
})
|
||||
|
||||
folderName.value = {} as Folder
|
||||
@ -624,7 +624,7 @@ async function upload(file: any): Promise<null | undefined> {
|
||||
}
|
||||
|
||||
xhr.value.upload.onerror = () => {
|
||||
indexStore.msgAlert('alert-danger', `Upload error: ${xhr.value.status}`, 3)
|
||||
indexStore.msgAlert('alert-error', `Upload error: ${xhr.value.status}`, 3)
|
||||
|
||||
resolve(undefined)
|
||||
}
|
||||
|
@ -352,7 +352,7 @@ async function savePreset() {
|
||||
if (response.status === 200) {
|
||||
indexStore.msgAlert('alert-success', 'Save Preset done!', 2)
|
||||
} else {
|
||||
indexStore.msgAlert('alert-danger', 'Save Preset failed!', 2)
|
||||
indexStore.msgAlert('alert-error', 'Save Preset failed!', 2)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -389,7 +389,7 @@ async function createNewPreset() {
|
||||
indexStore.msgAlert('alert-success', 'Save Preset done!', 2)
|
||||
getPreset(-1)
|
||||
} else {
|
||||
indexStore.msgAlert('alert-danger', 'Save Preset failed!', 2)
|
||||
indexStore.msgAlert('alert-error', 'Save Preset failed!', 2)
|
||||
}
|
||||
}
|
||||
|
||||
@ -427,7 +427,7 @@ async function submitMessage() {
|
||||
if (response.status === 200) {
|
||||
indexStore.msgAlert('alert-success', 'Sending success...', 2)
|
||||
} else {
|
||||
indexStore.msgAlert('alert-danger', 'Sending failed...', 2)
|
||||
indexStore.msgAlert('alert-error', 'Sending failed...', 2)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -1116,7 +1116,7 @@ async function onSubmitImport(evt: any) {
|
||||
playlistStore.getPlaylist(listDate.value)
|
||||
})
|
||||
.catch((e: string) => {
|
||||
indexStore.msgAlert('alert-danger', e, 4)
|
||||
indexStore.msgAlert('alert-error', e, 4)
|
||||
})
|
||||
|
||||
playlistIsLoading.value = false
|
||||
@ -1155,7 +1155,7 @@ async function generatePlaylist() {
|
||||
indexStore.msgAlert('alert-success', 'Generate Playlist done...', 2)
|
||||
})
|
||||
.catch((e: any) => {
|
||||
indexStore.msgAlert('alert-danger', e.data ? e.data : e, 4)
|
||||
indexStore.msgAlert('alert-error', e.data ? e.data : e, 4)
|
||||
})
|
||||
|
||||
// reset selections
|
||||
@ -1194,7 +1194,7 @@ async function savePlaylist(saveDate: string) {
|
||||
if (e.status === 409) {
|
||||
indexStore.msgAlert('alert-warning', e.data, 2)
|
||||
} else {
|
||||
indexStore.msgAlert('alert-danger', e, 4)
|
||||
indexStore.msgAlert('alert-error', e, 4)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
6
plugins/bootstrap.client.js
vendored
6
plugins/bootstrap.client.js
vendored
@ -1,6 +0,0 @@
|
||||
import bootstrap from 'bootstrap/dist/js/bootstrap.bundle.js'
|
||||
import "bootstrap-icons/font/bootstrap-icons.css";
|
||||
|
||||
export default defineNuxtPlugin((nuxtApp) => {
|
||||
nuxtApp.provide('bootstrap', bootstrap)
|
||||
})
|
@ -74,7 +74,7 @@ export const useConfig = defineStore('config', {
|
||||
},
|
||||
]
|
||||
|
||||
indexStore.msgAlert('alert-danger', e, 3)
|
||||
indexStore.msgAlert('alert-error', e, 3)
|
||||
})
|
||||
},
|
||||
|
||||
@ -143,7 +143,7 @@ export const useConfig = defineStore('config', {
|
||||
this.configPlayout = data
|
||||
})
|
||||
.catch(() => {
|
||||
indexStore.msgAlert('alert-danger', 'No playout config found!', 3)
|
||||
indexStore.msgAlert('alert-error', 'No playout config found!', 3)
|
||||
})
|
||||
},
|
||||
|
||||
|
@ -32,7 +32,7 @@ export const useMedia = defineStore('media', {
|
||||
if (response.status === 200) {
|
||||
return response.json()
|
||||
} else {
|
||||
indexStore.msgAlert('alert-danger', 'Storage not exist!', 3)
|
||||
indexStore.msgAlert('alert-error', 'Storage not exist!', 3)
|
||||
|
||||
return {
|
||||
source: '',
|
||||
|
106
tailwind.config.ts
Normal file
106
tailwind.config.ts
Normal file
@ -0,0 +1,106 @@
|
||||
module.exports = {
|
||||
theme: {
|
||||
extend: {
|
||||
borderWidth: {
|
||||
title: '0.1rem',
|
||||
},
|
||||
boxShadow: {
|
||||
'3xl': '0 1em 5em rgba(0, 0, 0, 0.3)',
|
||||
},
|
||||
colors: {
|
||||
'my-gray': 'var(--my-gray)',
|
||||
'my-gray-text': 'var(--my-gray-text)',
|
||||
'my-text': 'var(--my-text)',
|
||||
'my-shadow': 'var(--my-shadow)',
|
||||
'my-shadow-dark': 'var(--my-shadow-dark)',
|
||||
'my-black': '#141414',
|
||||
'link-hover': 'var(--link-hover)',
|
||||
'light-accent': 'var(--light-accent)',
|
||||
'base-100-odd': 'var(--base-100-odd)',
|
||||
},
|
||||
fontFamily: {
|
||||
sans: ['Source Sans Pro', 'Segoe UI', 'Helvetica Neue', 'Arial', 'sans-serif'],
|
||||
},
|
||||
fontSize: {
|
||||
xxs: '10px',
|
||||
sm: '14px',
|
||||
base: '15px',
|
||||
lg: '20px',
|
||||
xl: '24px',
|
||||
},
|
||||
screens: {
|
||||
xxs: '374px',
|
||||
xs: '500px',
|
||||
'2sm': '825px',
|
||||
'2md': '876px',
|
||||
'4xl': { min: '1971px' },
|
||||
},
|
||||
transitionProperty: {
|
||||
height: 'height',
|
||||
},
|
||||
},
|
||||
},
|
||||
daisyui: {
|
||||
themes: [
|
||||
{
|
||||
light: {
|
||||
'color-scheme': 'light',
|
||||
primary: '#e0e0e0',
|
||||
'base-content': '#222222',
|
||||
secondary: '#c7c7c7',
|
||||
accent: '#f28c1b',
|
||||
'base-100': '#ffffff',
|
||||
'base-200': '#F2F5F7',
|
||||
'base-300': '#E5E6E6',
|
||||
neutral: '#2B3440',
|
||||
'neutral-focus': '#343232',
|
||||
info: '#0000ff',
|
||||
success: '#008000',
|
||||
warning: '#f28c1b',
|
||||
error: '#ff3c00',
|
||||
'--base-100': '#ffffff',
|
||||
'--base-200': '#F2F5F7',
|
||||
'--base-300': '#E5E6E6',
|
||||
'--base-100-odd': '#ececec',
|
||||
'--my-accent': '#f28c1b',
|
||||
'--link-hover': '#f4ae61',
|
||||
'--my-gray': '#707070',
|
||||
'--my-gray-text': '#6a6a6a',
|
||||
'--my-text': '#141414',
|
||||
'--my-shadow': '#e6e6e6',
|
||||
'--my-shadow-dark': '#9f9f9f',
|
||||
'--light-accent': '#9c6a0c',
|
||||
},
|
||||
dark: {
|
||||
'color-scheme': 'dark',
|
||||
primary: '#3b3b3b',
|
||||
'base-content': '#DFDFDF',
|
||||
secondary: '#d3d3d3',
|
||||
accent: '#f28c1b',
|
||||
'base-100': '#313131',
|
||||
'base-200': '#222222',
|
||||
'base-300': '#1c1c1c',
|
||||
neutral: '#272626',
|
||||
'neutral-focus': '#343232',
|
||||
info: '#0000ff',
|
||||
success: '#008000',
|
||||
warning: '#f28c1b',
|
||||
error: '#ff3c00',
|
||||
'--base-100': '#313131',
|
||||
'--base-200': '#222222',
|
||||
'--base-300': '#1c1c1c',
|
||||
'--base-100-odd': '#3d3d3d',
|
||||
'--my-accent': '#f28c1b',
|
||||
'--link-hover': '#f4ae61',
|
||||
'--my-gray': '#aaaaaa',
|
||||
'--my-gray-text': '#bababa',
|
||||
'--my-text': '#eeeeee',
|
||||
'--my-shadow': '#111',
|
||||
'--my-shadow-dark': '#000',
|
||||
'--light-accent': '#f1a312',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
plugins: [require('daisyui')],
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user