8 Commits
Author SHA1 Message Date
walamana 87812d02d3 feat: show notification to install miniplan as app
Deploy Miniplan / build (push) Successful in 59s
2026-07-21 16:38:05 +02:00
walamana 1104280c0c fix: upgrade node in Dockerfile to v24
Deploy Miniplan / build (push) Successful in 2m7s
2026-07-21 14:02:57 +02:00
walamana 2e829b281b fix: upgrade all dependencies and resolve conflicts
Deploy Miniplan / build (push) Failing after 1m16s
2026-07-21 14:00:22 +02:00
walamana 7adeb41402 fix: add missing dependency
Deploy Miniplan / build (push) Failing after 1m2s
2026-07-21 13:49:29 +02:00
walamana c7f3fb0e6e fix: update package-lock.json
Deploy Miniplan / build (push) Failing after 52s
2026-07-21 13:47:57 +02:00
walamana ccabdc580c fix: build
Deploy Miniplan / build (push) Failing after 54s
2026-07-21 13:45:46 +02:00
walamana 0350b80cda feat: make miniplan a PWA
Deploy Miniplan / build (push) Failing after 1m20s
2026-07-21 13:41:18 +02:00
walamana 2dac1598ff fix: use common sql syntax for dropping null constraint on birthday field
Deploy Miniplan / build (push) Successful in 2m58s
2026-07-14 22:59:35 +02:00
24 changed files with 6476 additions and 836 deletions
+1
View File
@@ -2,3 +2,4 @@
.idea
node_modules
package-lock.json
/.idea/
+1 -1
View File
@@ -4,7 +4,7 @@ COPY --chown=gradle:gradle /private/minis-backend /home/gradle/src
WORKDIR /home/gradle/src
RUN gradle buildFatJar --no-daemon
FROM node:18 as build_frontend
FROM node:24 as build_frontend
RUN mkdir -p /home/node/app/node_modules && chown -R node:node /home/node/app
WORKDIR /home/node/app
+1 -4
View File
@@ -8,8 +8,5 @@
},
"author": "",
"license": "ISC",
"dependencies": {
"dropzone-vue3": "^1.0.2",
"rxjs": "^7.8.1"
}
"dependencies": {}
}
@@ -30,7 +30,7 @@ fun Application.configureDatabases() {
SchemaUtils.create(Gottesdienste)
SchemaUtils.create(Marks)
// TODO Add proper versioning and migration for database
exec("ALTER TABLE MINISTRANTEN ALTER COLUMN birthday BIGINT NULL;")
exec("ALTER TABLE MINISTRANTEN ALTER COLUMN birthday DROP NOT NULL;")
}
}
+1
View File
@@ -26,3 +26,4 @@ coverage
*.njsproj
*.sln
*.sw?
/dev-dist/
+6 -1
View File
@@ -3,9 +3,14 @@
<head>
<meta charset="UTF-8">
<link rel="icon" href="/favicon.ico">
<link rel="apple-touch-icon" href="/minis_logo.png">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="theme-color" content="#ffffff">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<meta name="apple-mobile-web-app-title" content="Miniplan">
<title>Miniplan App</title>
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200" rel="stylesheet" />
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200" rel="stylesheet">
</head>
<body>
+6256 -813
View File
File diff suppressed because it is too large Load Diff
+6 -2
View File
@@ -10,6 +10,7 @@
"type-check": "vue-tsc --noEmit -p tsconfig.app.json --composite false"
},
"dependencies": {
"crypto-js": "^4.2.0",
"dropzone-vue3": "^1.0.2",
"rxjs": "^7.8.1",
"underscore": "^1.13.7",
@@ -19,12 +20,15 @@
"devDependencies": {
"@tsconfig/node18": "^18.2.0",
"@types/node": "^18.17.0",
"@vite-pwa/assets-generator": "^1.0.2",
"@vitejs/plugin-vue": "^4.2.3",
"@vue/tsconfig": "^0.4.0",
"less": "^4.2.0",
"npm-run-all": "^4.1.5",
"typescript": "~5.1.6",
"vite": "^4.4.6",
"vue-tsc": "^1.8.6"
"vite": "^5.4.0",
"vite-plugin-pwa": "^1.3.0",
"vue-tsc": "^3.3.7",
"workbox-window": "^7.4.1"
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 931 B

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 891 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

+36
View File
@@ -6,6 +6,8 @@ import {onMounted, ref} from "vue";
import {Auth} from "@/services/auth";
import DialogHost from "@/components/dialog/DialogHost.vue";
import {LoginService} from "@/services/LoginService";
import {Dialogs} from "@/services/DialogService";
import InstallPWADialog from "@/components/dialog/InstallPWADialog.vue";
let showPopup = ref(false)
let loggedIn = ref(false)
@@ -33,6 +35,40 @@ LoginService.subject.subscribe(() => {
showPopup.value = true
})
const installEvent = ref<Event>()
window.addEventListener('beforeinstallprompt', (e) => {
e.preventDefault()
console.log("beforeinstallprompt")
installEvent.value = e
scheduleInstallDialog()
})
function scheduleInstallDialog() {
if (isPwa()) return
if (localStorage.getItem('installPromptDismissed')) return
if (!installEvent.value) return
Dialogs.createDialog(InstallPWADialog, {
onPositive(dontShowAgain: boolean) {
if (dontShowAgain) localStorage.setItem('installPromptDismissed', 'true')
;(installEvent.value as any).prompt()
},
onNegative(dontShowAgain: boolean) {
if (dontShowAgain) localStorage.setItem('installPromptDismissed', 'true')
},
onDismiss() {}
}, {})
}
function isPwa() {
return window.matchMedia('(display-mode: standalone)').matches
}
window.addEventListener('appinstalled', () => {
localStorage.setItem('installPromptDismissed', 'true')
})
</script>
<template>
File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 276 B

After

Width:  |  Height:  |  Size: 58 KiB

@@ -0,0 +1,66 @@
<script setup lang="ts">
import {ref} from "vue";
import Dialog from "@/components/dialog/Dialog.vue";
import type {DialogControls} from "@/components/dialog/dialog";
interface InstallPWADialogProps extends DialogControls {
}
defineProps<InstallPWADialogProps>()
const dontShowAgain = ref<boolean>(false)
</script>
<template>
<Dialog class="dialog">
<h3 style="margin-bottom: 10px; display: flex; gap: 1rem; align-items: center">
Zum Startbildschirm hinzufügen
</h3>
<p style="line-height: 1.7rem">
Dein Miniplan kann jetzt auf Ihrem Gerät installiert werden, um schnell und einfach darauf zuzugreifen.
</p>
<label class="checkbox">
<input type="checkbox" v-model="dontShowAgain" />
Nicht mehr anzeigen
</label>
<div class="buttons">
<button @click="onNegative(dontShowAgain)" class="dismiss">Später</button>
<button @click="onPositive(dontShowAgain)" ><i>browser_updated</i>Jetzt hinzufügen</button>
</div>
</Dialog>
</template>
<style scoped lang="less">
.dialog {
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
width: 500px
}
.checkbox {
display: flex;
align-items: center;
gap: 8px;
margin-top: 16px;
font-size: 14px;
cursor: pointer;
}
.buttons {
display: flex;
justify-content: end;
margin-top: 20px;
.dismiss {
background: transparent;
}
}
</style>
+13
View File
@@ -4,6 +4,19 @@ import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import { registerSW } from 'virtual:pwa-register'
registerSW({
onNeedRefresh() {
if (confirm('New content available. Reload?')) {
window.location.reload()
}
},
onOfflineReady() {
console.log('App ready to work offline')
}
})
const app = createApp(App)
app.use(router)
+67 -2
View File
@@ -2,11 +2,75 @@ import { fileURLToPath, URL } from 'node:url'
import {defineConfig} from 'vite'
import vue from '@vitejs/plugin-vue'
import {VitePWA} from 'vite-plugin-pwa'
// https://vitejs.dev/config/
export default defineConfig({
export default defineConfig(({mode}) => (
{
plugins: [
vue(),
VitePWA({
registerType: 'autoUpdate',
includeAssets: ['favicon.ico', 'minis_logo.png'],
manifest: {
name: 'Miniplan App',
short_name: 'Miniplan',
description: 'Lade deinen Miniplan direkt auf dein Handy. So hast du den Miniplan jederzeit und schnell erreichbar.',
theme_color: '#ffffff',
background_color: '#ffffff',
display: 'standalone',
scope: '/',
start_url: '/',
screenshots: [
{
src: 'screenshot1.png',
sizes: '505x710',
type: 'image/png',
form_factor: "narrow",
label: "Miniplan Liste"
},
{
src: 'screenshot2.png',
sizes: '505x711',
type: 'image/png',
form_factor: "narrow",
label: "Miniplan Tabelle"
},
{
src: "screenshot3.png",
sizes: '1003x709',
type: 'image/png',
form_factor: "wide",
label: "Miniplan Tabelle"
}
],
icons: [
{
src: "pwa-64x64.png",
sizes: "64x64",
type: "image/png"
},
{
src: "pwa-192x192.png",
sizes: "192x192",
type: "image/png"
},
{
src: "pwa-512x512.png",
sizes: "512x512",
type: "image/png"
},
{
src: "maskable-icon-512x512.png",
sizes: "512x512",
type: "image/png",
purpose: "maskable"
}
]
},
devOptions: {
enabled: mode === "development"
}
})
],
resolve: {
alias: {
@@ -14,3 +78,4 @@ export default defineConfig({
}
}
})
)