Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 70 additions & 2 deletions docs/backlog/initial-build-backlog.md
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,7 @@ Tasks:
- create `/events`;
- create `/events/{slug}` placeholder;
- create `/projects`;
- position `/projects` as a showcase for DDS-built tooling, software, plugins, apps, integrations, and selected community builds;
- create `/news`;
- create `/locations`;
- create `/about`;
Expand All @@ -273,6 +274,7 @@ Acceptance criteria:
- all public routes render;
- navigation uses `Events`, `Projects`, `News`, `About`, and `Contact`;
- pages can use placeholder content, but no generic theme copy;
- `/projects` placeholder copy clearly frames projects as a public showcase rather than internal project management;
- each page has a clear purpose, heading hierarchy, and primary next action.

### DDS-008: Baseline SEO And Redirect Shape
Expand Down Expand Up @@ -410,6 +412,69 @@ Acceptance criteria:
- imported WordPress media can be deduplicated by legacy ID;
- alt text is supported.

### DDS-014A: Project Showcase Model

Goal: model public showcase projects for DDS-built tooling, software, plugins, apps, integrations, and selected community builds.

Tasks:

- create `Project` model and migration;
- include title, slug, excerpt, content, status, project_type, visibility, featured flag, and sort order;
- support project types such as `plugin`, `app`, `integration`, `event_tooling`, `community_build`, and `other`;
- add optional links for GitHub, live demo, documentation, download, and contact;
- add optional ownership/credit fields for maintainers or contributors;
- add SEO fields and media references;
- include locale-aware content fields.

Acceptance criteria:

- RotorHazard plugins, TrackDraw-style apps, race tooling, livestream overlays, and community utilities can be represented cleanly;
- projects can distinguish open source, public platform, internal/private, and archived work;
- only published and public projects are visible on the public site;
- the model does not imply internal task or project-management workflows;
- project records can be created through factories/tests.

### DDS-014B: Public Project Showcase Pages

Goal: show DDS projects publicly as a credible development and tooling showcase.

Tasks:

- build `/projects` project index page;
- build `/projects/{slug}` project detail page;
- add filters or grouping by project type when useful;
- show project links, status, media, and concise value proposition;
- add useful empty states.

Acceptance criteria:

- `/projects` lists published public projects;
- `/projects/{slug}` shows a published public project;
- project cards communicate type, purpose, status, and primary link clearly;
- archived or internal-only projects are not publicly listed unless explicitly marked visible;
- pages can support both software/tooling projects and selected community builds without separate content types.

### DDS-014C: Admin Project CRUD

Goal: manage project showcase content from the `/dashboard` management area.

Tasks:

- create `/dashboard/projects` project index;
- create project create/edit forms;
- add server-side validation through Form Requests;
- add project policies;
- add publish status and featured controls;
- make link fields easy to add, remove, and validate.

Acceptance criteria:

- admins can create, edit, publish, feature, archive, and reorder projects;
- editors can create and update projects according to their seeded permissions;
- validation errors are shown clearly;
- public visibility follows status and visibility fields;
- forms are structured for concise project showcase editing, not task tracking.

## Epic 6: WordPress Import Spike

### DDS-015: WordPress Export Discovery
Expand Down Expand Up @@ -465,7 +530,10 @@ Acceptance criteria:
13. DDS-012
14. DDS-013
15. DDS-014
16. DDS-015
17. DDS-016
16. DDS-014A
17. DDS-014B
18. DDS-014C
19. DDS-015
20. DDS-016

The public website rebuild can begin once DDS-007 and DDS-010 exist, while admin and import work continue behind it.
5 changes: 3 additions & 2 deletions resources/js/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,17 @@ import { TooltipProvider } from '@/components/ui/tooltip';
import { initializeTheme } from '@/hooks/use-appearance';
import AppLayout from '@/layouts/app-layout';
import AuthLayout from '@/layouts/auth-layout';
import PublicLayout from '@/layouts/public-layout';
import SettingsLayout from '@/layouts/settings/layout';

const appName = import.meta.env.VITE_APP_NAME || 'Laravel';
const appName = import.meta.env.VITE_APP_NAME || 'Dutch Drone Squad';

createInertiaApp({
title: (title) => (title ? `${title} - ${appName}` : appName),
layout: (name) => {
switch (true) {
case name === 'welcome':
return null;
return PublicLayout;
case name.startsWith('auth/'):
return AuthLayout;
case name.startsWith('settings/'):
Expand Down
89 changes: 65 additions & 24 deletions resources/js/components/app-header.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
import { Link, usePage } from '@inertiajs/react';
import { BookOpen, Folder, LayoutGrid, Menu, Search } from 'lucide-react';
import {
CalendarDays,
ClipboardList,
Home,
LayoutDashboard,
LifeBuoy,
MapPin,
Menu,
Newspaper,
Search,
ShieldCheck,
} from 'lucide-react';
import AppLogo from '@/components/app-logo';
import AppLogoIcon from '@/components/app-logo-icon';
import { Breadcrumbs } from '@/components/breadcrumbs';
Expand Down Expand Up @@ -31,8 +42,8 @@ import {
import { UserMenuContent } from '@/components/user-menu-content';
import { useCurrentUrl } from '@/hooks/use-current-url';
import { useInitials } from '@/hooks/use-initials';
import { cn, toUrl } from '@/lib/utils';
import { dashboard } from '@/routes';
import { cn } from '@/lib/utils';
import { dashboard, home } from '@/routes';
import type { BreadcrumbItem, NavItem } from '@/types';

type Props = {
Expand All @@ -41,22 +52,47 @@ type Props = {

const mainNavItems: NavItem[] = [
{
title: 'Dashboard',
title: 'Overzicht',
href: dashboard(),
icon: LayoutGrid,
icon: LayoutDashboard,
},
{
title: 'Events',
href: `${dashboard.url()}#events`,
icon: CalendarDays,
},
{
title: 'Projecten',
href: `${dashboard.url()}#projects`,
icon: ClipboardList,
},
{
title: 'Nieuws',
href: `${dashboard.url()}#news`,
icon: Newspaper,
},
{
title: 'Locaties',
href: `${dashboard.url()}#locations`,
icon: MapPin,
},
];

const rightNavItems: NavItem[] = [
{
title: 'Repository',
href: 'https://github.com/laravel/react-starter-kit',
icon: Folder,
title: 'Publieke site',
href: home(),
icon: Home,
},
{
title: 'Huisregels',
href: `${dashboard.url()}#house-rules`,
icon: ShieldCheck,
},
{
title: 'Documentation',
href: 'https://laravel.com/docs/starter-kits#react',
icon: BookOpen,
title: 'Support',
href: `${dashboard.url()}#support`,
icon: LifeBuoy,
},
];

Expand All @@ -81,6 +117,7 @@ export function AppHeader({ breadcrumbs = [] }: Props) {
variant="ghost"
size="icon"
className="mr-2 h-[34px] w-[34px]"
aria-label="Open navigatiemenu"
>
<Menu className="h-5 w-5" />
</Button>
Expand All @@ -90,10 +127,17 @@ export function AppHeader({ breadcrumbs = [] }: Props) {
className="flex h-full w-64 flex-col items-stretch justify-between bg-sidebar"
>
<SheetTitle className="sr-only">
Navigation menu
Navigatiemenu
</SheetTitle>
<SheetHeader className="flex justify-start text-left">
<AppLogoIcon className="h-6 w-6 fill-current text-black dark:text-white" />
<div className="flex items-center gap-2">
<span className="flex size-8 items-center justify-center rounded-md bg-red-600 text-white dark:bg-red-500 dark:text-neutral-950">
<AppLogoIcon className="size-5 fill-current" />
</span>
<span className="font-semibold">
Dutch Drone Squad
</span>
</div>
</SheetHeader>
<div className="flex h-full flex-1 flex-col space-y-4 p-4">
<div className="flex h-full flex-col justify-between text-sm">
Expand All @@ -114,18 +158,16 @@ export function AppHeader({ breadcrumbs = [] }: Props) {

<div className="flex flex-col space-y-4">
{rightNavItems.map((item) => (
<a
<Link
key={item.title}
href={toUrl(item.href)}
target="_blank"
rel="noopener noreferrer"
href={item.href}
className="flex items-center space-x-2 font-medium"
>
{item.icon && (
<item.icon className="h-5 w-5" />
)}
<span>{item.title}</span>
</a>
</Link>
))}
</div>
</div>
Expand Down Expand Up @@ -182,17 +224,16 @@ export function AppHeader({ breadcrumbs = [] }: Props) {
variant="ghost"
size="icon"
className="group h-9 w-9 cursor-pointer"
aria-label="Zoeken"
>
<Search className="!size-5 opacity-80 group-hover:opacity-100" />
</Button>
<div className="ml-1 hidden gap-1 lg:flex">
{rightNavItems.map((item) => (
<Tooltip key={item.title}>
<TooltipTrigger>
<a
href={toUrl(item.href)}
target="_blank"
rel="noopener noreferrer"
<TooltipTrigger asChild>
<Link
href={item.href}
className="group inline-flex h-9 w-9 items-center justify-center rounded-md bg-transparent p-0 text-sm font-medium text-accent-foreground ring-offset-background transition-colors hover:bg-accent hover:text-accent-foreground focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:outline-none disabled:pointer-events-none disabled:opacity-50"
>
<span className="sr-only">
Expand All @@ -201,7 +242,7 @@ export function AppHeader({ breadcrumbs = [] }: Props) {
{item.icon && (
<item.icon className="size-5 opacity-80 group-hover:opacity-100" />
)}
</a>
</Link>
</TooltipTrigger>
<TooltipContent>
<p>{item.title}</p>
Expand Down
4 changes: 2 additions & 2 deletions resources/js/components/app-logo-icon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ import type { SVGAttributes } from 'react';

export default function AppLogoIcon(props: SVGAttributes<SVGElement>) {
return (
<svg {...props} viewBox="0 0 40 42" xmlns="http://www.w3.org/2000/svg">
<svg {...props} viewBox="0 0 40 40" xmlns="http://www.w3.org/2000/svg">
<path
fillRule="evenodd"
clipRule="evenodd"
d="M17.2 5.63325L8.6 0.855469L0 5.63325V32.1434L16.2 41.1434L32.4 32.1434V23.699L40 19.4767V9.85547L31.4 5.07769L22.8 9.85547V18.2999L17.2 21.411V5.63325ZM38 18.2999L32.4 21.411V15.2545L38 12.1434V18.2999ZM36.9409 10.4439L31.4 13.5221L25.8591 10.4439L31.4 7.36561L36.9409 10.4439ZM24.8 18.2999V12.1434L30.4 15.2545V21.411L24.8 18.2999ZM23.8 20.0323L29.3409 23.1105L16.2 30.411L10.6591 27.3328L23.8 20.0323ZM7.6 27.9212L15.2 32.1434V38.2999L2 30.9666V7.92116L7.6 11.0323V27.9212ZM8.6 9.29991L3.05913 6.22165L8.6 3.14339L14.1409 6.22165L8.6 9.29991ZM30.4 24.8101L17.2 32.1434V38.2999L30.4 30.9666V24.8101ZM9.6 11.0323L15.2 7.92117V22.5221L9.6 25.6333V11.0323Z"
d="M20 3.5C22.1 3.5 23.8 5.2 23.8 7.3V16.2H32.7C34.8 16.2 36.5 17.9 36.5 20C36.5 22.1 34.8 23.8 32.7 23.8H23.8V32.7C23.8 34.8 22.1 36.5 20 36.5C17.9 36.5 16.2 34.8 16.2 32.7V23.8H7.3C5.2 23.8 3.5 22.1 3.5 20C3.5 17.9 5.2 16.2 7.3 16.2H16.2V7.3C16.2 5.2 17.9 3.5 20 3.5ZM18.5 18.5H21.5V21.5H18.5V18.5ZM7.3 18.9C6.7 18.9 6.2 19.4 6.2 20C6.2 20.6 6.7 21.1 7.3 21.1H14.7V18.9H7.3ZM18.9 7.3V14.7H21.1V7.3C21.1 6.7 20.6 6.2 20 6.2C19.4 6.2 18.9 6.7 18.9 7.3ZM25.3 18.9V21.1H32.7C33.3 21.1 33.8 20.6 33.8 20C33.8 19.4 33.3 18.9 32.7 18.9H25.3ZM18.9 25.3V32.7C18.9 33.3 19.4 33.8 20 33.8C20.6 33.8 21.1 33.3 21.1 32.7V25.3H18.9Z"
/>
</svg>
);
Expand Down
9 changes: 6 additions & 3 deletions resources/js/components/app-logo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,15 @@ import AppLogoIcon from '@/components/app-logo-icon';
export default function AppLogo() {
return (
<>
<div className="flex aspect-square size-8 items-center justify-center rounded-md bg-sidebar-primary text-sidebar-primary-foreground">
<AppLogoIcon className="size-5 fill-current text-white dark:text-black" />
<div className="flex aspect-square size-8 items-center justify-center rounded-md bg-red-600 text-white shadow-sm dark:bg-red-500 dark:text-neutral-950">
<AppLogoIcon className="size-5 fill-current" />
</div>
<div className="ml-1 grid flex-1 text-left text-sm">
<span className="mb-0.5 truncate leading-tight font-semibold">
Laravel Starter Kit
Dutch Drone Squad
</span>
<span className="truncate text-xs leading-tight text-neutral-500 dark:text-neutral-400">
Platform
</span>
</div>
</>
Expand Down
54 changes: 44 additions & 10 deletions resources/js/components/app-sidebar.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
import { Link } from '@inertiajs/react';
import { BookOpen, FolderGit2, LayoutGrid } from 'lucide-react';
import {
CalendarDays,
ClipboardList,
Home,
LayoutDashboard,
LifeBuoy,
MapPin,
Newspaper,
ShieldCheck,
} from 'lucide-react';
import AppLogo from '@/components/app-logo';
import { NavFooter } from '@/components/nav-footer';
import { NavMain } from '@/components/nav-main';
Expand All @@ -13,27 +22,52 @@ import {
SidebarMenuButton,
SidebarMenuItem,
} from '@/components/ui/sidebar';
import { dashboard } from '@/routes';
import { dashboard, home } from '@/routes';
import type { NavItem } from '@/types';

const mainNavItems: NavItem[] = [
{
title: 'Dashboard',
title: 'Overzicht',
href: dashboard(),
icon: LayoutGrid,
icon: LayoutDashboard,
},
{
title: 'Events',
href: `${dashboard.url()}#events`,
icon: CalendarDays,
},
{
title: 'Projecten',
href: `${dashboard.url()}#projects`,
icon: ClipboardList,
},
{
title: 'Nieuws',
href: `${dashboard.url()}#news`,
icon: Newspaper,
},
{
title: 'Locaties',
href: `${dashboard.url()}#locations`,
icon: MapPin,
},
];

const footerNavItems: NavItem[] = [
{
title: 'Repository',
href: 'https://github.com/laravel/react-starter-kit',
icon: FolderGit2,
title: 'Publieke site',
href: home(),
icon: Home,
},
{
title: 'Huisregels',
href: `${dashboard.url()}#house-rules`,
icon: ShieldCheck,
},
{
title: 'Documentation',
href: 'https://laravel.com/docs/starter-kits#react',
icon: BookOpen,
title: 'Support',
href: `${dashboard.url()}#support`,
icon: LifeBuoy,
},
];

Expand Down
Loading