Files
SkinbaseNova/resources/js/Pages/Studio/StudioNewsTaxonomies.jsx

115 lines
8.8 KiB
JavaScript

import React, { useState } from 'react'
import { router, useForm, usePage } from '@inertiajs/react'
import StudioLayout from '../../Layouts/StudioLayout'
function replacePattern(pattern, token, value) {
return String(pattern || '').replace(token, String(value))
}
export default function StudioNewsTaxonomies() {
const { props } = usePage()
const [categories, setCategories] = useState(Array.isArray(props.categories) ? props.categories : [])
const [tags, setTags] = useState(Array.isArray(props.tags) ? props.tags : [])
const categoryForm = useForm({ name: '', slug: '', description: '', position: 0, is_active: true })
const tagForm = useForm({ name: '', slug: '' })
const updateCategory = (index, field, value) => {
setCategories((current) => current.map((item, itemIndex) => (itemIndex === index ? { ...item, [field]: value } : item)))
}
const updateTag = (index, field, value) => {
setTags((current) => current.map((item, itemIndex) => (itemIndex === index ? { ...item, [field]: value } : item)))
}
const saveCategory = (category) => {
router.patch(replacePattern(props.updateCategoryUrlPattern, '__CATEGORY__', category.id), category, { preserveScroll: true })
}
const saveTag = (tag) => {
router.patch(replacePattern(props.updateTagUrlPattern, '__TAG__', tag.id), tag, { preserveScroll: true })
}
return (
<StudioLayout title={props.title} subtitle={props.description}>
<section className="rounded-[28px] border border-white/10 bg-white/[0.03] p-5">
<div className="flex flex-wrap gap-3 text-sm font-semibold">
<a href="/studio/news/categories" className={`rounded-full px-4 py-2 ${props.activeTab === 'categories' ? 'border border-sky-300/20 bg-sky-400/10 text-sky-100' : 'border border-white/10 bg-white/[0.04] text-white'}`}>Categories</a>
<a href="/studio/news/tags" className={`rounded-full px-4 py-2 ${props.activeTab === 'tags' ? 'border border-sky-300/20 bg-sky-400/10 text-sky-100' : 'border border-white/10 bg-white/[0.04] text-white'}`}>Tags</a>
<a href="/studio/news" className="rounded-full border border-white/10 bg-white/[0.04] px-4 py-2 text-white">Back to newsroom</a>
</div>
</section>
<div className="mt-6 grid gap-6 xl:grid-cols-2">
<section className="rounded-[28px] border border-white/10 bg-white/[0.03] p-5">
<div className="flex items-center justify-between gap-3">
<div>
<h2 className="text-xl font-semibold text-white">Categories</h2>
<p className="mt-1 text-sm text-slate-400">Stable editorial buckets for the newsroom.</p>
</div>
<span className="text-sm text-slate-500">{categories.length} total</span>
</div>
<form onSubmit={(event) => { event.preventDefault(); categoryForm.post(props.storeCategoryUrl) }} className="mt-5 grid gap-3">
<div className="grid gap-3 md:grid-cols-2">
<input value={categoryForm.data.name} onChange={(event) => categoryForm.setData('name', event.target.value)} placeholder="Category name" className="rounded-2xl border border-white/10 bg-black/20 px-4 py-3 text-white outline-none" />
<input value={categoryForm.data.slug} onChange={(event) => categoryForm.setData('slug', event.target.value)} placeholder="optional slug" className="rounded-2xl border border-white/10 bg-black/20 px-4 py-3 text-white outline-none" />
</div>
<textarea value={categoryForm.data.description} onChange={(event) => categoryForm.setData('description', event.target.value)} rows={3} placeholder="Description" className="rounded-[24px] border border-white/10 bg-black/20 px-4 py-3 text-white outline-none" />
<div className="flex flex-wrap items-center gap-3">
<input type="number" value={categoryForm.data.position} onChange={(event) => categoryForm.setData('position', event.target.value)} min="0" className="w-28 rounded-2xl border border-white/10 bg-black/20 px-4 py-3 text-white outline-none" />
<label className="flex items-center gap-2 text-sm text-white"><input type="checkbox" checked={categoryForm.data.is_active} onChange={(event) => categoryForm.setData('is_active', event.target.checked)} /> Active</label>
<button type="submit" className="rounded-full border border-sky-300/20 bg-sky-400/10 px-4 py-2 text-sm font-semibold text-sky-100">Create category</button>
</div>
</form>
<div className="mt-6 grid gap-3">
{categories.map((category, index) => (
<div key={category.id} className="rounded-[24px] border border-white/10 bg-black/20 p-4">
<div className="grid gap-3 md:grid-cols-2">
<input value={category.name} onChange={(event) => updateCategory(index, 'name', event.target.value)} className="rounded-2xl border border-white/10 bg-black/20 px-4 py-3 text-white outline-none" />
<input value={category.slug} onChange={(event) => updateCategory(index, 'slug', event.target.value)} className="rounded-2xl border border-white/10 bg-black/20 px-4 py-3 text-white outline-none" />
</div>
<textarea value={category.description || ''} onChange={(event) => updateCategory(index, 'description', event.target.value)} rows={2} className="mt-3 w-full rounded-[24px] border border-white/10 bg-black/20 px-4 py-3 text-white outline-none" />
<div className="mt-3 flex flex-wrap items-center gap-3 text-sm text-slate-300">
<input type="number" value={category.position || 0} min="0" onChange={(event) => updateCategory(index, 'position', event.target.value)} className="w-24 rounded-2xl border border-white/10 bg-black/20 px-4 py-3 text-white outline-none" />
<label className="flex items-center gap-2"><input type="checkbox" checked={Boolean(category.is_active)} onChange={(event) => updateCategory(index, 'is_active', event.target.checked)} /> Active</label>
<span className="text-xs uppercase tracking-[0.14em] text-slate-500">{Number(category.published_count || 0).toLocaleString()} published</span>
<button type="button" onClick={() => saveCategory(category)} className="rounded-full border border-white/10 bg-white/[0.05] px-4 py-2 text-sm font-semibold text-white">Save</button>
</div>
</div>
))}
</div>
</section>
<section className="rounded-[28px] border border-white/10 bg-white/[0.03] p-5">
<div className="flex items-center justify-between gap-3">
<div>
<h2 className="text-xl font-semibold text-white">Tags</h2>
<p className="mt-1 text-sm text-slate-400">Flexible labels for search, discovery, and internal linking.</p>
</div>
<span className="text-sm text-slate-500">{tags.length} total</span>
</div>
<form onSubmit={(event) => { event.preventDefault(); tagForm.post(props.storeTagUrl) }} className="mt-5 grid gap-3 md:grid-cols-[minmax(0,1fr)_minmax(0,1fr)_auto] md:items-center">
<input value={tagForm.data.name} onChange={(event) => tagForm.setData('name', event.target.value)} placeholder="Tag name" className="rounded-2xl border border-white/10 bg-black/20 px-4 py-3 text-white outline-none" />
<input value={tagForm.data.slug} onChange={(event) => tagForm.setData('slug', event.target.value)} placeholder="optional slug" className="rounded-2xl border border-white/10 bg-black/20 px-4 py-3 text-white outline-none" />
<button type="submit" className="rounded-full border border-sky-300/20 bg-sky-400/10 px-4 py-3 text-sm font-semibold text-sky-100">Create tag</button>
</form>
<div className="mt-6 grid gap-3">
{tags.map((tag, index) => (
<div key={tag.id} className="rounded-[24px] border border-white/10 bg-black/20 p-4">
<div className="grid gap-3 md:grid-cols-[minmax(0,1fr)_minmax(0,1fr)_auto_auto] md:items-center">
<input value={tag.name} onChange={(event) => updateTag(index, 'name', event.target.value)} className="rounded-2xl border border-white/10 bg-black/20 px-4 py-3 text-white outline-none" />
<input value={tag.slug} onChange={(event) => updateTag(index, 'slug', event.target.value)} className="rounded-2xl border border-white/10 bg-black/20 px-4 py-3 text-white outline-none" />
<span className="text-xs uppercase tracking-[0.14em] text-slate-500">{Number(tag.published_count || 0).toLocaleString()} published</span>
<button type="button" onClick={() => saveTag(tag)} className="rounded-full border border-white/10 bg-white/[0.05] px-4 py-2 text-sm font-semibold text-white">Save</button>
</div>
</div>
))}
</div>
</section>
</div>
</StudioLayout>
)
}