Compare commits
17 Commits
b2b27e06fe
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 110cd52a95 | |||
| e54e0c4cf3 | |||
| 6ad37399e0 | |||
| 0bcaf2c499 | |||
| 1a99fc6572 | |||
| c0660049f4 | |||
| 0acf354473 | |||
| 3301b595de | |||
| b919c22cd7 | |||
| ba7de85d65 | |||
| 5a536e34d4 | |||
| 22d24e0f7b | |||
| 45adb0b137 | |||
| 14c71a07c0 | |||
| 5ec214565c | |||
| 854e80cddd | |||
| eeed7497cb |
5
.dockerignore
Normal file
5
.dockerignore
Normal file
@@ -0,0 +1,5 @@
|
||||
.idea
|
||||
.git
|
||||
.nuxt
|
||||
.output
|
||||
node_modules
|
||||
32
Dockerfile
32
Dockerfile
@@ -1,25 +1,29 @@
|
||||
# This dockerfile is to turn .output into a docker image
|
||||
# You still need to install the project on your machine and build it.
|
||||
# Only then you run this docker file
|
||||
# Build Stage 1
|
||||
|
||||
# This project by default uses pnpm.
|
||||
# If you wish to use npm, delete "pnpm-lock.yaml" first.
|
||||
FROM node:22-alpine AS build
|
||||
WORKDIR /app
|
||||
|
||||
# 1. pnpm (or npm) i # If you get GNU problems, use pnpm (or npm) i --force
|
||||
# 2. pnpm (or npm) run build # Sit back while it compiles
|
||||
# 3. sudo docker build . -t myapp:latest (replace 'myapp' to whatever tag you wanna give your image)
|
||||
RUN corepack enable
|
||||
|
||||
# You should have the image now !
|
||||
# Now you just need to create the container
|
||||
# Copy package.json and your lockfile, here we add pnpm-lock.yaml for illustration
|
||||
COPY package.json pnpm-lock.yaml ./
|
||||
|
||||
# Here is an example command for creating a container
|
||||
# sudo docker run -d --name myappcontainer --network host -e PORT=3000 myapp:latest
|
||||
# Install dependencies
|
||||
RUN pnpm i
|
||||
|
||||
# Copy the entire project
|
||||
COPY . ./
|
||||
|
||||
# Build the project
|
||||
RUN pnpm run build
|
||||
|
||||
# Build Stage 2
|
||||
|
||||
FROM node:22-alpine
|
||||
WORKDIR /app
|
||||
|
||||
# Copy the build's result
|
||||
COPY .output/ .
|
||||
# Only `.output` folder is needed from the build stage
|
||||
COPY --from=build /app/.output/ ./
|
||||
|
||||
# Change the port and host
|
||||
ENV PORT=3000
|
||||
|
||||
30
Dockerfile.output.bak
Normal file
30
Dockerfile.output.bak
Normal file
@@ -0,0 +1,30 @@
|
||||
# This dockerfile is to turn .output into a docker image
|
||||
# You still need to install the project on your machine and build it.
|
||||
# Only then you run this docker file
|
||||
|
||||
# This project by default uses pnpm.
|
||||
# If you wish to use npm, delete "pnpm-lock.yaml" first.
|
||||
|
||||
# 1. pnpm (or npm) i # If you get GNU problems, use pnpm (or npm) i --force
|
||||
# 2. pnpm (or npm) run build # Sit back while it compiles
|
||||
# 3. sudo docker build . -t myapp:latest (replace 'myapp' to whatever tag you wanna give your image)
|
||||
|
||||
# You should have the image now !
|
||||
# Now you just need to create the container
|
||||
|
||||
# Here is an example command for creating a container
|
||||
# sudo docker run -d --name myappcontainer --network host -e PORT=3000 myapp:latest
|
||||
|
||||
FROM node:22-alpine
|
||||
WORKDIR /app
|
||||
|
||||
# Copy the build's result
|
||||
COPY .output/ .
|
||||
|
||||
# Change the port and host
|
||||
ENV PORT=3000
|
||||
ENV HOST=0.0.0.0
|
||||
|
||||
EXPOSE 3000
|
||||
|
||||
CMD ["node", "/app/server/index.mjs"]
|
||||
@@ -3,17 +3,30 @@
|
||||
@import "tailwindcss";
|
||||
@import "@nuxt/ui";
|
||||
|
||||
|
||||
:root {
|
||||
--font-sans: 'Public Sans', sans-serif;
|
||||
--ui-bg: rgb(220,220,220);
|
||||
--font-sans: 'Space Grotesk', sans-serif;
|
||||
/*--font-sans: 'Public Sans', sans-serif; */
|
||||
}
|
||||
|
||||
.light {
|
||||
--ui-bg: var(--color-stone-100);
|
||||
--ui-primary: var(--color-stone-100);
|
||||
--sidebar-bg: oklch(85% 0.001 106.424);
|
||||
}
|
||||
|
||||
.dark {
|
||||
/*--ui-bg: var(--ui-color-neutral-950);*/
|
||||
--ui-bg: rgb(17,17,17);
|
||||
--ui-bg-accented: rgba(189, 23, 255, 0.3);
|
||||
--ui-bg-elevated: rgba(189, 23, 255, 0.2);
|
||||
--ui-bg: oklch(16.7% 0.004 49.25);
|
||||
--ui-primary: var(--color-neutral-900);
|
||||
--sidebar-bg: oklch(18.7% 0.004 49.25);
|
||||
}
|
||||
|
||||
--ui-primary: rgb(189, 23, 255);
|
||||
.sidebar-light {
|
||||
--ui-bg: var(--color-stone-300);
|
||||
}
|
||||
.sidebar-dark {
|
||||
background-color: var(--color-stone-800);
|
||||
}
|
||||
|
||||
.dark-widget-bg {
|
||||
|
||||
37
app/assets/css/ui_colors.css
Normal file
37
app/assets/css/ui_colors.css
Normal file
@@ -0,0 +1,37 @@
|
||||
|
||||
.ui-red {
|
||||
--ui-primary: var(--color-red-500);
|
||||
--ui-bg-elevated: oklch(63.7% 0.237 25.331 / 20%);
|
||||
--ui-bg-accented: oklch(63.7% 0.237 25.331 / 40%);
|
||||
}
|
||||
|
||||
.ui-blue {
|
||||
--ui-primary: var(--color-blue-600);
|
||||
--ui-bg-elevated: oklch(54.6% 0.245 262.881 / 20%);
|
||||
--ui-bg-accented: oklch(54.6% 0.245 262.881 / 40%);
|
||||
}
|
||||
.ui-green {
|
||||
--ui-primary: var(--color-green-500);
|
||||
--ui-bg-elevated: oklch(72.3% 0.219 149.579 / 20%);
|
||||
--ui-bg-accented: oklch(72.3% 0.219 149.579 / 40%);
|
||||
}
|
||||
.ui-violet {
|
||||
--ui-primary: var(--color-violet-500);
|
||||
--ui-bg-elevated: oklch(60.6% 0.25 292.717 / 20%);
|
||||
--ui-bg-accented: oklch(60.6% 0.25 292.717 / 40%);
|
||||
}
|
||||
|
||||
.ui-indigo {
|
||||
--ui-primary: var(--color-indigo-500);
|
||||
--ui-bg-elevated: oklch(58.5% 0.233 277.117 / 20%);
|
||||
--ui-bg-accented: oklch(58.5% 0.233 277.117 / 40%);
|
||||
}
|
||||
|
||||
.ui-orange {
|
||||
--ui-primary: var(--color-orange-600);
|
||||
}
|
||||
.ui-cyan {
|
||||
--ui-primary: var(--color-cyan-500);
|
||||
--ui-bg-elevated: oklch(71.5% 0.143 215.221 / 20%);
|
||||
--ui-bg-accented: oklch(71.5% 0.143 215.221 / 40%);
|
||||
}
|
||||
12
app/assets/css/ui_fonts.css
Normal file
12
app/assets/css/ui_fonts.css
Normal file
@@ -0,0 +1,12 @@
|
||||
.font-pubsans {
|
||||
--font-sans: "Public Sans", sans-serif;
|
||||
}
|
||||
.font-geist {
|
||||
--font-sans: "Geist One", sans-serif;
|
||||
}
|
||||
.font-nunito {
|
||||
--font-sans: "Nunito", sans-serif;
|
||||
}
|
||||
.font-grot {
|
||||
--font-sans: "Space Grotesk", sans-serif;
|
||||
}
|
||||
@@ -12,7 +12,7 @@ import { literal, string } from 'zod';
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UHeader :title="props.title" toggle-side="left">
|
||||
<UHeader :title="props.title" toggle-side="left" :to="undefined">
|
||||
<template #toggle>
|
||||
<!---<UDashboardSidebarCollapse />-->
|
||||
<UDashboardSidebarToggle />
|
||||
|
||||
11
app/components/dashboard/settings/nav_header.vue
Normal file
11
app/components/dashboard/settings/nav_header.vue
Normal file
@@ -0,0 +1,11 @@
|
||||
<script setup lang="ts">
|
||||
const items = [
|
||||
{
|
||||
|
||||
}
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<
|
||||
</template>
|
||||
@@ -10,7 +10,7 @@ const items_def: NavigationMenuItem[][] = [
|
||||
active: true
|
||||
}, {
|
||||
label: 'Inbox',
|
||||
icon: 'i-lucide-inbox',
|
||||
icon: 'solar:chat-round-unread-outline',
|
||||
badge: '4'
|
||||
}, {
|
||||
label: 'Contacts',
|
||||
|
||||
22
app/composables/ui_cookies.ts
Normal file
22
app/composables/ui_cookies.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import type {CookieRef} from "#app";
|
||||
|
||||
function ui_cookie(name:string, def:string,prefix:string): CookieRef<string> {
|
||||
|
||||
const c = useCookie(name,{
|
||||
default: () => def,
|
||||
|
||||
});
|
||||
watch(c, (newVal, oldVal) => {
|
||||
// remove old class (if it's been put)
|
||||
if (oldVal != undefined) document.body.classList.remove(prefix + oldVal.toLowerCase());
|
||||
|
||||
// add new class
|
||||
document.body.classList.add(prefix + newVal.toLowerCase());
|
||||
}, { immediate: true})
|
||||
|
||||
return c;
|
||||
|
||||
}
|
||||
|
||||
export function ui_color_cookie() { return ui_cookie("ui_color","Green","ui-"); }
|
||||
export function ui_font_cookie() { return ui_cookie("ui_font","pubsans","font-"); }
|
||||
67
app/layouts/dash_settings.vue
Normal file
67
app/layouts/dash_settings.vue
Normal file
@@ -0,0 +1,67 @@
|
||||
<script setup lang="ts">
|
||||
import type { NavigationMenuItem } from '@nuxt/ui'
|
||||
|
||||
const nav : NavigationMenuItem[][] = [[
|
||||
{
|
||||
label: 'Home',
|
||||
icon: 'i-lucide-house',
|
||||
to: "/dashboard"
|
||||
},
|
||||
{
|
||||
label: 'Notifications',
|
||||
icon: 'solar:chat-round-unread-outline',
|
||||
badge: '4',
|
||||
to: "/events"
|
||||
}, {
|
||||
label: 'Settings',
|
||||
icon: 'i-lucide-settings',
|
||||
//to: '/settings',
|
||||
children: [
|
||||
{
|
||||
label: "General",
|
||||
icon: "solar:settings-minimalistic-broken",
|
||||
to: "/settings",
|
||||
},
|
||||
{
|
||||
label: "Profile",
|
||||
icon: "solar:user-broken",
|
||||
to: "/settings/profile"
|
||||
},
|
||||
{
|
||||
label: "Theme",
|
||||
icon: "solar:pallete-2-broken",
|
||||
to: "/settings/theme"
|
||||
},
|
||||
],
|
||||
}]
|
||||
];
|
||||
|
||||
const route = useRoute();
|
||||
const title = ref("...");
|
||||
|
||||
watch(route, (newVal, oldVal) => {
|
||||
title.value = (newVal.meta.title as string) || "Settings";
|
||||
}, {immediate: true});
|
||||
//const title = pageMeta.title || "Settings"
|
||||
|
||||
onMounted(() => {
|
||||
ui_color_cookie();
|
||||
})
|
||||
|
||||
</script>
|
||||
<template>
|
||||
<UDashboardGroup>
|
||||
<DashboardSidebar class="bg-(--sidebar-bg)" collapsible :resizable="undefined" :items="nav"/>
|
||||
|
||||
<UDashboardSidebarCollapse />
|
||||
|
||||
<UContainer class="max-w-full overflow-auto">
|
||||
<DashboardHeader :title="title"/>
|
||||
<UNavigationMenu :items="nav[0][2].children" />
|
||||
<!-- in my case I want the button to be the same everywhere.
|
||||
if you wanna customize it per page, place it per page inside UContainer -->
|
||||
<!--<UDashboardSidebarToggle variant="subtle" class="absolute z-20 top-3 left-2.5"/>-->
|
||||
<slot />
|
||||
</UContainer>
|
||||
</UDashboardGroup>
|
||||
</template>>
|
||||
@@ -8,27 +8,56 @@ const nav : NavigationMenuItem[][] = [[
|
||||
to: "/dashboard"
|
||||
}, {
|
||||
label: 'Notifications',
|
||||
icon: 'i-lucide-inbox',
|
||||
icon: 'solar:chat-round-unread-outline',
|
||||
badge: '4',
|
||||
to: "/events"
|
||||
}, {
|
||||
label: 'Settings',
|
||||
icon: 'i-lucide-settings',
|
||||
to: '/settings'
|
||||
}]];
|
||||
//to: '/settings',
|
||||
children: [
|
||||
{
|
||||
label: "General",
|
||||
icon: "solar:settings-minimalistic-broken",
|
||||
to: "/settings",
|
||||
},
|
||||
{
|
||||
label: "Profile",
|
||||
icon: "solar:user-broken",
|
||||
to: "/settings/profile"
|
||||
},
|
||||
{
|
||||
label: "Theme",
|
||||
icon: "solar:pallete-2-broken",
|
||||
to: "/settings/theme"
|
||||
},
|
||||
],
|
||||
}]
|
||||
];
|
||||
/*[
|
||||
{
|
||||
label: "Feedback"
|
||||
}
|
||||
]];*/
|
||||
|
||||
// set color
|
||||
/*useHead({
|
||||
bodyAttrs: {
|
||||
class: "ui-blue"
|
||||
}
|
||||
})*/
|
||||
onMounted(() => {
|
||||
ui_color_cookie();
|
||||
})
|
||||
|
||||
</script>
|
||||
<template>
|
||||
<UDashboardGroup>
|
||||
<DashboardSidebar collapsible resizable :items="nav"/>
|
||||
<DashboardSidebar class="bg-(--sidebar-bg)" collapsible :resizable="undefined" :items="nav"/>
|
||||
|
||||
<UDashboardSidebarCollapse />
|
||||
|
||||
<UContainer class="overflow-auto">
|
||||
<UContainer class="max-w-full overflow-auto">
|
||||
<!-- in my case I want the button to be the same everywhere.
|
||||
if you wanna customize it per page, place it per page inside UContainer -->
|
||||
<!--<UDashboardSidebarToggle variant="subtle" class="absolute z-20 top-3 left-2.5"/>-->
|
||||
|
||||
@@ -5,9 +5,9 @@
|
||||
<template>
|
||||
<!-- the whole content must be in a UContainer -->
|
||||
<!-- overflow auto allows scroll -->
|
||||
<UContainer class="overflow-auto">
|
||||
<!--<UContainer class="overflow-auto">-->
|
||||
<slot />
|
||||
</UContainer>
|
||||
<!--</UContainer>-->
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
@@ -123,7 +123,7 @@ import { h } from 'vue'
|
||||
|
||||
<template>
|
||||
<DashboardHeader title="Dashboard"/>
|
||||
|
||||
<UContainer class="max-w-full">
|
||||
<div class="mt-3 mb-3 flex flex-col w-full relative z-0">
|
||||
<ProseH2 class="text-center m-0.5 w-full">Hey there, user!</ProseH2>
|
||||
<ProseP class="text-center text-muted m-0.5">Welcome to the UI!</ProseP>
|
||||
@@ -144,8 +144,7 @@ import { h } from 'vue'
|
||||
|
||||
|
||||
<UTable :data="table_demo" :columns="table_headers" class="flex-1 "/>
|
||||
|
||||
|
||||
</UContainer>
|
||||
<UFooter>
|
||||
<p>footer</p>
|
||||
</UFooter>
|
||||
|
||||
@@ -1,30 +1,11 @@
|
||||
<script setup lang="ts">
|
||||
definePageMeta({
|
||||
layout: "dashboard"
|
||||
layout: "dash-settings",
|
||||
title: "Settings"
|
||||
})
|
||||
const settings_submenu = ref([
|
||||
{
|
||||
label: "Profile",
|
||||
icon: "i-lucide-user-round",
|
||||
to: "/settings/profile"
|
||||
},
|
||||
{
|
||||
label: "Security",
|
||||
icon: "i-lucide-lock",
|
||||
to: "/settings/security"
|
||||
},
|
||||
{
|
||||
label: "Aspect",
|
||||
icon: "i-lucide-palette",
|
||||
to: "/settings/ui"
|
||||
}
|
||||
])
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<DashboardHeader title="Settings" />
|
||||
|
||||
<UNavigationMenu :items="settings_submenu" />
|
||||
|
||||
|
||||
</template>
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
<script setup lang="ts">
|
||||
definePageMeta({
|
||||
layout: "dashboard"
|
||||
layout: "dash-settings"
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<DashboardHeader title="Profile" />
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
<script setup lang="ts">
|
||||
definePageMeta({
|
||||
layout: "dashboard"
|
||||
layout: "dash-settings"
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<DashboardHeader title="Security" />
|
||||
|
||||
|
||||
</template>
|
||||
|
||||
|
||||
63
app/pages/settings/theme.vue
Normal file
63
app/pages/settings/theme.vue
Normal file
@@ -0,0 +1,63 @@
|
||||
<script setup lang="ts">
|
||||
import type {CookieRef} from "#app";
|
||||
|
||||
definePageMeta({
|
||||
layout: "dash-settings",
|
||||
title: "Customization"
|
||||
})
|
||||
|
||||
const colors_list = ref(["Cyan","Red","Green","Blue","Purple"])
|
||||
const color = ref("...");
|
||||
|
||||
const fonts_list = ref(["Public Sans","Space Grotesk", "Geist"])
|
||||
const font = ref("...");
|
||||
|
||||
const ready = ref(false);
|
||||
|
||||
onMounted(() => {
|
||||
color.value = ui_color_cookie().value
|
||||
ready.value = true
|
||||
})
|
||||
|
||||
watch(color, (newVal, oldVal) => {
|
||||
ui_color_cookie().value = newVal;
|
||||
})
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
<UContainer class="mt-4 max-w-full justify-center">
|
||||
<UFormField
|
||||
label="Main Color"
|
||||
orientation="horizontal"
|
||||
description="Select the main color of the UI"
|
||||
size="lg"
|
||||
class="flex justify-between w-full mt-3 mb-3"
|
||||
:disabled="!ready"
|
||||
>
|
||||
<USelect v-model="color" :items="colors_list" />
|
||||
</UFormField>
|
||||
<UFormField
|
||||
label="Mode"
|
||||
orientation="horizontal"
|
||||
description="Select if you want Light or Dark mode"
|
||||
size="lg"
|
||||
class="flex justify-between w-full mt-3 mb-3"
|
||||
>
|
||||
<UColorModeSelect />
|
||||
</UFormField>
|
||||
<UFormField
|
||||
label="Font"
|
||||
orientation="horizontal"
|
||||
description="Select which font you want"
|
||||
size="lg"
|
||||
class="flex justify-between w-full mt-3 mb-3"
|
||||
:disabled="!ready"
|
||||
>
|
||||
<USelect v-model="font" :items="fonts_list" />
|
||||
</UFormField>
|
||||
</UContainer>
|
||||
</template>
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
definePageMeta({
|
||||
layout: "dashboard"
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<DashboardHeader title="Customization" />
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -1,10 +1,11 @@
|
||||
import tailwindcss from "@tailwindcss/vite";
|
||||
|
||||
// https://nuxt.com/docs/api/configuration/nuxt-config
|
||||
// @ts-ignore
|
||||
export default defineNuxtConfig({
|
||||
compatibilityDate: '2025-07-15',
|
||||
devtools: {enabled: true},
|
||||
css: ['./app/assets/css/global.css' ],
|
||||
css: ['@/assets/css/global.css', '@/assets/css/ui_colors.css', /*'@/assets/css/ui_fonts.css' */],
|
||||
modules: [
|
||||
'@nuxt/image',
|
||||
'@nuxt/ui',
|
||||
@@ -14,7 +15,7 @@ export default defineNuxtConfig({
|
||||
],
|
||||
vite: {
|
||||
plugins: [
|
||||
tailwindcss()
|
||||
tailwindcss(),
|
||||
],
|
||||
},
|
||||
})
|
||||
@@ -18,7 +18,6 @@
|
||||
"@tailwindcss/vite": "^4.1.17",
|
||||
"@vueuse/core": "^10.0.0",
|
||||
"@vueuse/head": "github:vueuse/head",
|
||||
"nuxt": "^4.2.1",
|
||||
"nuxt-auth-utils": "0.5.25",
|
||||
"nuxt-storm": "^1.1.3",
|
||||
"tailwindcss": "^4.1.17",
|
||||
@@ -29,6 +28,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@iconify-json/lucide": "^1.2.77",
|
||||
"@nuxtjs/tailwindcss": "^6.14.0"
|
||||
"@nuxtjs/tailwindcss": "^6.14.0",
|
||||
"nuxt": "^4.2.2"
|
||||
}
|
||||
}
|
||||
|
||||
1287
pnpm-lock.yaml
generated
1287
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user