feat: Vorlagen-Ansicht (Admin) + Typ Vorlage in Projektseite
- neuer Typ Vorlage (nur Admin waehlbar), Badge, isTemplate - Dropdown-Filter Vorlagen nur fuer Admins; Vorlagen aus normalen Ansichten ausgeblendet - in Vorlagen-Ansicht: Vorlagen sehen + previewen Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -9,10 +9,12 @@ import {
|
|||||||
FaGlobe,
|
FaGlobe,
|
||||||
FaLock,
|
FaLock,
|
||||||
FaCircleInfo,
|
FaCircleInfo,
|
||||||
|
FaClone,
|
||||||
} from 'react-icons/fa6'
|
} from 'react-icons/fa6'
|
||||||
import { useWebsiteProjects } from '../hooks/useWebsiteProjects'
|
import { useWebsiteProjects } from '../hooks/useWebsiteProjects'
|
||||||
import { useCustomers } from '../hooks/useCustomers'
|
import { useCustomers } from '../hooks/useCustomers'
|
||||||
import { useWorkorders } from '../hooks/useWorkorders'
|
import { useWorkorders } from '../hooks/useWorkorders'
|
||||||
|
import { useAuth } from '../context/AuthContext'
|
||||||
import {
|
import {
|
||||||
createProjectFromTemplate,
|
createProjectFromTemplate,
|
||||||
syncGiteaRepos,
|
syncGiteaRepos,
|
||||||
@@ -49,6 +51,14 @@ const TYPES = {
|
|||||||
icon: <FaGlobe />,
|
icon: <FaGlobe />,
|
||||||
hint: 'Oeffentlich erreichbar ohne Login unter <subdomain>.project.webklar.com',
|
hint: 'Oeffentlich erreichbar ohne Login unter <subdomain>.project.webklar.com',
|
||||||
},
|
},
|
||||||
|
template: {
|
||||||
|
label: 'Vorlage',
|
||||||
|
short: 'Vorlage',
|
||||||
|
badge: 'badge-warn',
|
||||||
|
icon: <FaClone />,
|
||||||
|
hint: 'Website-Vorlage (login-geschuetzt) - erscheint nur fuer Admins in der Vorlagen-Ansicht',
|
||||||
|
adminOnly: true,
|
||||||
|
},
|
||||||
project: {
|
project: {
|
||||||
label: 'Internes Projekt (kein Hosting)',
|
label: 'Internes Projekt (kein Hosting)',
|
||||||
short: 'Projekt',
|
short: 'Projekt',
|
||||||
@@ -59,6 +69,7 @@ const TYPES = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function typeOf(project) {
|
function typeOf(project) {
|
||||||
|
if (project.isTemplate) return 'template'
|
||||||
if (project.isPublic) return 'website'
|
if (project.isPublic) return 'website'
|
||||||
if (project.subdomain) return 'preview'
|
if (project.subdomain) return 'preview'
|
||||||
return 'project'
|
return 'project'
|
||||||
@@ -68,6 +79,7 @@ export default function ProjectsPage() {
|
|||||||
const { projects, loading, error, fetchAllProjects, assignProjects, unassignProject } = useWebsiteProjects()
|
const { projects, loading, error, fetchAllProjects, assignProjects, unassignProject } = useWebsiteProjects()
|
||||||
const { customers } = useCustomers()
|
const { customers } = useCustomers()
|
||||||
const { workorders } = useWorkorders(WORKORDER_FILTERS)
|
const { workorders } = useWorkorders(WORKORDER_FILTERS)
|
||||||
|
const { isAdmin } = useAuth()
|
||||||
const [filter, setFilter] = useState('all')
|
const [filter, setFilter] = useState('all')
|
||||||
const [showCreateModal, setShowCreateModal] = useState(false)
|
const [showCreateModal, setShowCreateModal] = useState(false)
|
||||||
const [createForm, setCreateForm] = useState({
|
const [createForm, setCreateForm] = useState({
|
||||||
@@ -110,18 +122,21 @@ export default function ProjectsPage() {
|
|||||||
}, [workorders])
|
}, [workorders])
|
||||||
|
|
||||||
const counts = useMemo(() => {
|
const counts = useMemo(() => {
|
||||||
const active = projects.filter((p) => !p.archived)
|
// Vorlagen sind aus den normalen Ansichten ausgeblendet (eigene Ansicht, nur Admin)
|
||||||
|
const active = projects.filter((p) => !p.archived && !p.isTemplate)
|
||||||
return {
|
return {
|
||||||
all: active.length,
|
all: active.length,
|
||||||
unassigned: active.filter((p) => !p.customerId).length,
|
unassigned: active.filter((p) => !p.customerId).length,
|
||||||
assigned: active.filter((p) => Boolean(p.customerId)).length,
|
assigned: active.filter((p) => Boolean(p.customerId)).length,
|
||||||
archived: projects.filter((p) => p.archived).length,
|
archived: projects.filter((p) => p.archived).length,
|
||||||
|
templates: projects.filter((p) => p.isTemplate && !p.archived).length,
|
||||||
}
|
}
|
||||||
}, [projects])
|
}, [projects])
|
||||||
|
|
||||||
const filteredProjects = useMemo(() => {
|
const filteredProjects = useMemo(() => {
|
||||||
|
if (filter === 'templates') return projects.filter((p) => p.isTemplate && !p.archived)
|
||||||
if (filter === 'archived') return projects.filter((p) => p.archived)
|
if (filter === 'archived') return projects.filter((p) => p.archived)
|
||||||
const active = projects.filter((p) => !p.archived)
|
const active = projects.filter((p) => !p.archived && !p.isTemplate)
|
||||||
if (filter === 'unassigned') return active.filter((p) => !p.customerId)
|
if (filter === 'unassigned') return active.filter((p) => !p.customerId)
|
||||||
if (filter === 'assigned') return active.filter((p) => Boolean(p.customerId))
|
if (filter === 'assigned') return active.filter((p) => Boolean(p.customerId))
|
||||||
return active
|
return active
|
||||||
@@ -245,9 +260,14 @@ export default function ProjectsPage() {
|
|||||||
{ value: 'all', label: `Alle Projekte (${counts.all})` },
|
{ value: 'all', label: `Alle Projekte (${counts.all})` },
|
||||||
{ value: 'unassigned', label: `Unzugeordnet (${counts.unassigned})` },
|
{ value: 'unassigned', label: `Unzugeordnet (${counts.unassigned})` },
|
||||||
{ value: 'assigned', label: `Zugeordnet (${counts.assigned})` },
|
{ value: 'assigned', label: `Zugeordnet (${counts.assigned})` },
|
||||||
|
// Vorlagen-Ansicht nur fuer Admins
|
||||||
|
...(isAdmin ? [{ value: 'templates', label: `Vorlagen (${counts.templates})` }] : []),
|
||||||
{ value: 'archived', label: `Archiviert (${counts.archived})` },
|
{ value: 'archived', label: `Archiviert (${counts.archived})` },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
// Typ-Optionen im Bearbeiten-Modal: adminOnly-Typen (Vorlage) nur fuer Admins
|
||||||
|
const typeOptions = Object.entries(TYPES).filter(([, meta]) => isAdmin || !meta.adminOnly)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="main-content">
|
<div className="main-content">
|
||||||
<header className="text-center mb-2">
|
<header className="text-center mb-2">
|
||||||
@@ -488,7 +508,7 @@ export default function ProjectsPage() {
|
|||||||
value={editForm.projectType}
|
value={editForm.projectType}
|
||||||
onChange={(e) => setEditForm((p) => ({ ...p, projectType: e.target.value }))}
|
onChange={(e) => setEditForm((p) => ({ ...p, projectType: e.target.value }))}
|
||||||
>
|
>
|
||||||
{Object.entries(TYPES).map(([key, meta]) => (
|
{typeOptions.map(([key, meta]) => (
|
||||||
<option key={key} value={key}>{meta.label}</option>
|
<option key={key} value={key}>{meta.label}</option>
|
||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
|
|||||||
Reference in New Issue
Block a user