feat(share): Nur-Ansicht-Teilen-Link (live, optional PIN) + QR (v1.6.9)
All checks were successful
Build and Push Docker Image / build-and-push (push) Successful in 31m39s
All checks were successful
Build and Push Docker Image / build-and-push (push) Successful in 31m39s
Neuer öffentlicher Teilen-Link für die aktuelle Lage — ohne Login nur zum Ansehen, live aktualisiert. Optional mit 4–8-stelliger PIN geschützt. - DB: Project.shareToken (unique) / sharePin (bcrypt) / shareEnabled + Migration - API: POST/GET /api/projects/[id]/share (Verwaltung, Mandanten-Check, VIEWER blockiert), GET/POST /api/share/[token] (öffentlich, PIN-Prüfung, Rate-Limit gegen Brute-Force) - Öffentliche Seite /view/[token]: PIN-Gate, read-only MapView, Socket-Live + 30s-Poll - Teilen-Dialog im Werkzeuge-/Mobil-Menü: Link kopieren, QR-Code, PIN setzen/entfernen, Teilen beenden (widerruft den Link) - Middleware: /api/share/ öffentlich freigeschaltet Hinweis: hochgeladene Custom-Symbole zeigen im anonymen Viewer den Platzhalter (Icon-Endpunkte bleiben auth-geschützt); eingebaute taktische Zeichen erscheinen normal. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -47,8 +47,10 @@ import {
|
||||
Plus,
|
||||
ChevronDown,
|
||||
Loader2,
|
||||
Share2,
|
||||
} from 'lucide-react'
|
||||
import { HoseSettingsDialog } from '@/components/dialogs/hose-settings-dialog'
|
||||
import { ShareDialog } from '@/components/dialogs/share-dialog'
|
||||
import { degToCompass } from '@/components/map/map-compass'
|
||||
import type { Project, DrawFeature, ProjectMode } from '@/types'
|
||||
import { formatDateTime } from '@/lib/utils'
|
||||
@@ -119,6 +121,7 @@ export function Topbar({
|
||||
}: TopbarProps) {
|
||||
const [isLoadDialogOpen, setIsLoadDialogOpen] = useState(false)
|
||||
const [isHoseSettingsOpen, setIsHoseSettingsOpen] = useState(false)
|
||||
const [isShareOpen, setIsShareOpen] = useState(false)
|
||||
const [showPasswordDialog, setShowPasswordDialog] = useState(false)
|
||||
const [showDeleteAccountDialog, setShowDeleteAccountDialog] = useState(false)
|
||||
const [deleteAccountPw, setDeleteAccountPw] = useState('')
|
||||
@@ -293,6 +296,12 @@ export function Topbar({
|
||||
<List className="w-4 h-4 mr-2" />
|
||||
Einsätze verwalten
|
||||
</DropdownMenuItem>
|
||||
{project && (
|
||||
<DropdownMenuItem onClick={() => setIsShareOpen(true)}>
|
||||
<Share2 className="w-4 h-4 mr-2" />
|
||||
Lage teilen (Nur-Ansicht)
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem onClick={() => handleExport('png')}>
|
||||
<Download className="w-4 h-4 mr-2" />
|
||||
@@ -417,6 +426,12 @@ export function Topbar({
|
||||
<List className="w-4 h-4 mr-2" />
|
||||
Einsätze verwalten
|
||||
</DropdownMenuItem>
|
||||
{project && (
|
||||
<DropdownMenuItem onClick={() => setIsShareOpen(true)}>
|
||||
<Share2 className="w-4 h-4 mr-2" />
|
||||
Lage teilen (Nur-Ansicht)
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
|
||||
<DropdownMenuSeparator />
|
||||
|
||||
@@ -641,6 +656,8 @@ export function Topbar({
|
||||
|
||||
<HoseSettingsDialog open={isHoseSettingsOpen} onOpenChange={setIsHoseSettingsOpen} />
|
||||
|
||||
<ShareDialog open={isShareOpen} onOpenChange={setIsShareOpen} projectId={project?.id ?? null} projectTitle={project?.title} />
|
||||
|
||||
{/* Password Change Dialog */}
|
||||
<Dialog open={showPasswordDialog} onOpenChange={(open) => {
|
||||
setShowPasswordDialog(open)
|
||||
|
||||
Reference in New Issue
Block a user