Compare commits

...

4 Commits

6 changed files with 69 additions and 14 deletions

View File

@@ -11,6 +11,7 @@ RUN npm install
COPY . .
# Use a temporary DB for generation
ENV DATABASE_URL="file:./temp.db"
RUN npx prisma generate
RUN npm run build

View File

@@ -3,7 +3,7 @@
import { useState, useEffect } from "react"
import { format, eachDayOfInterval, isSameDay } from "date-fns"
import { de, enUS } from "date-fns/locale"
import { CalendarIcon, User, Home, X, Info, Utensils, Trash2, Check } from "lucide-react"
import { CalendarIcon, User, Home, X, Info, Utensils, Trash2, Check, Share2 } from "lucide-react"
import { toast } from "sonner"
import { Button } from "@/components/ui/button"
@@ -220,6 +220,25 @@ export function PlanDashboard({ plan, dict, settingsDict, lang }: PlanDashboardP
dict={settingsDict}
lang={lang}
/>
<Button variant="outline" size="sm" onClick={() => {
if (navigator.share) {
navigator.share({
title: plan.title,
text: dict.shareTitle,
url: window.location.href,
}).catch(() => {
// Fallback if share fails / is cancelled
navigator.clipboard.writeText(window.location.href)
toast.success(dict.copySuccess)
})
} else {
navigator.clipboard.writeText(window.location.href)
toast.success(dict.copySuccess)
}
}}>
<Share2 className="w-4 h-4 mr-2" />
{dict.share}
</Button>
</div>
</div>

View File

@@ -43,3 +43,7 @@ export async function unsubscribeUser(endpoint: string) {
return { success: true };
}
export async function getVapidPublicKey() {
return process.env.NEXT_PUBLIC_VAPID_PUBLIC_KEY;
}

View File

@@ -3,7 +3,7 @@
import { useState, useEffect } from "react"
import { Bell, BellOff, Loader2, AlertTriangle } from "lucide-react"
import { toast } from "sonner"
import { subscribeUser, unsubscribeUser } from "@/app/actions/subscription"
import { subscribeUser, unsubscribeUser, getVapidPublicKey } from "@/app/actions/subscription"
import { Button } from "@/components/ui/button"
function urlBase64ToUint8Array(base64String: string) {
@@ -31,25 +31,44 @@ export function PushSubscriptionSettings({ planId }: { planId: string }) {
const [subscription, setSubscription] = useState<PushSubscription | null>(null)
const [loading, setLoading] = useState(false)
const [debugInfo, setDebugInfo] = useState<string | null>(null)
const [vapidKey, setVapidKey] = useState<string | null>(null)
useEffect(() => {
const checks = []
if (!('serviceWorker' in navigator)) checks.push("No Service Worker support")
if (!('PushManager' in window)) checks.push("No PushManager support")
if (!process.env.NEXT_PUBLIC_VAPID_PUBLIC_KEY) checks.push("Missing VAPID Key")
async function checkSupport() {
const checks = []
if (!('serviceWorker' in navigator)) checks.push("No Service Worker support")
if (!('PushManager' in window)) checks.push("No PushManager support")
if (checks.length === 0) {
setIsSupported(true)
registerServiceWorker()
} else {
console.warn("Push not supported:", checks.join(", "))
setDebugInfo(checks.join(", "))
try {
const key = await getVapidPublicKey()
if (!key) {
checks.push("Missing VAPID Key (Server)")
} else {
setVapidKey(key)
}
} catch (e) {
checks.push("Failed to fetch VAPID Key")
}
if (checks.length === 0) {
setIsSupported(true)
registerServiceWorker()
} else {
console.warn("Push not supported:", checks.join(", "))
setDebugInfo(checks.join(", "))
}
}
checkSupport()
}, [])
async function registerServiceWorker() {
try {
const registration = await navigator.serviceWorker.ready
// Explicitly register the service worker
const registration = await navigator.serviceWorker.register('/push-sw.js')
// Wait for it to be ready
await navigator.serviceWorker.ready
const sub = await registration.pushManager.getSubscription()
setSubscription(sub)
} catch (e: any) {
@@ -59,6 +78,10 @@ export function PushSubscriptionSettings({ planId }: { planId: string }) {
}
async function subscribe() {
if (!vapidKey) {
toast.error("VAPID Key missing")
return
}
setLoading(true)
setDebugInfo(null)
try {
@@ -84,7 +107,7 @@ export function PushSubscriptionSettings({ planId }: { planId: string }) {
const sub = await registration.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: urlBase64ToUint8Array(process.env.NEXT_PUBLIC_VAPID_PUBLIC_KEY!)
applicationServerKey: urlBase64ToUint8Array(vapidKey)
})
console.log("Subscribed locally:", sub)

View File

@@ -38,6 +38,10 @@
"instructionsTitle": "Katzenpflege-Anleitungen",
"export": "Exportieren",
"settings": "Einstellungen",
"share": "Teilen",
"shareTitle": "Plan teilen",
"copySuccess": "Link kopiert!",
"shareError": "Fehler beim Teilen.",
"feeding": "Füttern",
"litter": "Klo reinigen",
"ownerHome": "Besitzer zu Hause",

View File

@@ -38,6 +38,10 @@
"instructionsTitle": "Cat Care Instructions",
"export": "Export",
"settings": "Settings",
"share": "Share",
"shareTitle": "Share Plan",
"copySuccess": "Link copied to clipboard!",
"shareError": "Could not share plan.",
"feeding": "Feeding",
"litter": "Clean litter",
"ownerHome": "Owner Home",