Wire admin studio SSR and search infrastructure
This commit is contained in:
@@ -3,6 +3,7 @@ import { Head, usePage } from '@inertiajs/react'
|
||||
import CollectionCard from '../../components/profile/collections/CollectionCard'
|
||||
import CollectionVisibilityBadge from '../../components/profile/collections/CollectionVisibilityBadge'
|
||||
import Checkbox from '../../components/ui/Checkbox'
|
||||
import DateTimePicker from '../../components/ui/DateTimePicker'
|
||||
import NovaSelect from '../../components/ui/NovaSelect'
|
||||
|
||||
function getCsrfToken() {
|
||||
@@ -671,17 +672,21 @@ function SmartRuleRow({
|
||||
{rule.field === 'created_at' ? (
|
||||
<Field label="Date Range">
|
||||
<div className="grid gap-3 sm:grid-cols-2">
|
||||
<input
|
||||
type="date"
|
||||
<DateTimePicker
|
||||
value={rule.value?.from || ''}
|
||||
onChange={(event) => onValueChange({ ...(rule.value || {}), from: event.target.value })}
|
||||
className="w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none transition focus:border-sky-300/35 focus:bg-white/[0.06]"
|
||||
onChange={(nextValue) => onValueChange({ ...(rule.value || {}), from: nextValue })}
|
||||
mode="date"
|
||||
placeholder="From date"
|
||||
clearable
|
||||
className="bg-white/[0.04]"
|
||||
/>
|
||||
<input
|
||||
type="date"
|
||||
<DateTimePicker
|
||||
value={rule.value?.to || ''}
|
||||
onChange={(event) => onValueChange({ ...(rule.value || {}), to: event.target.value })}
|
||||
className="w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none transition focus:border-sky-300/35 focus:bg-white/[0.06]"
|
||||
onChange={(nextValue) => onValueChange({ ...(rule.value || {}), to: nextValue })}
|
||||
mode="date"
|
||||
placeholder="To date"
|
||||
clearable
|
||||
className="bg-white/[0.04]"
|
||||
/>
|
||||
</div>
|
||||
</Field>
|
||||
@@ -2138,19 +2143,19 @@ export default function CollectionManage() {
|
||||
|
||||
<div className="grid gap-5 md:grid-cols-2">
|
||||
<Field label="Publish At" help="Leave empty to publish immediately. A future time keeps it off public surfaces until it goes live.">
|
||||
<input type="datetime-local" value={form.published_at} onChange={(event) => updateForm('published_at', event.target.value)} className="w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none transition focus:border-sky-300/35 focus:bg-white/[0.06]" />
|
||||
<DateTimePicker value={form.published_at} onChange={(nextValue) => updateForm('published_at', nextValue)} placeholder="Publish time" clearable className="bg-white/[0.04]" />
|
||||
</Field>
|
||||
<Field label="Unpublish At" help="Optional automatic sunset time for seasonal or editorial collections.">
|
||||
<input type="datetime-local" value={form.unpublished_at} onChange={(event) => updateForm('unpublished_at', event.target.value)} className="w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none transition focus:border-sky-300/35 focus:bg-white/[0.06]" />
|
||||
<DateTimePicker value={form.unpublished_at} onChange={(nextValue) => updateForm('unpublished_at', nextValue)} placeholder="Unpublish time" clearable className="bg-white/[0.04]" />
|
||||
</Field>
|
||||
</div>
|
||||
|
||||
<div className="grid gap-5 md:grid-cols-2">
|
||||
<Field label="Archive At" help="Optional timestamp for moving the collection to long-term archive workflows.">
|
||||
<input type="datetime-local" value={form.archived_at} onChange={(event) => updateForm('archived_at', event.target.value)} className="w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none transition focus:border-sky-300/35 focus:bg-white/[0.06]" />
|
||||
<DateTimePicker value={form.archived_at} onChange={(nextValue) => updateForm('archived_at', nextValue)} placeholder="Archive time" clearable className="bg-white/[0.04]" />
|
||||
</Field>
|
||||
<Field label="Expire At" help="Optional hard expiry for promotional or seasonal collections.">
|
||||
<input type="datetime-local" value={form.expired_at} onChange={(event) => updateForm('expired_at', event.target.value)} className="w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none transition focus:border-sky-300/35 focus:bg-white/[0.06]" />
|
||||
<DateTimePicker value={form.expired_at} onChange={(nextValue) => updateForm('expired_at', nextValue)} placeholder="Expiry time" clearable className="bg-white/[0.04]" />
|
||||
</Field>
|
||||
</div>
|
||||
</AdvancedSection>
|
||||
@@ -2734,7 +2739,7 @@ export default function CollectionManage() {
|
||||
<div className="flex min-w-0 flex-1 flex-col gap-2 md:min-w-[240px]">
|
||||
<NovaSelect value={inviteExpiryMode} onChange={(val) => setInviteExpiryMode(val)} searchable={false} options={[{ value: 'default', label: `Default (${inviteExpiryDays} days)` }, ...inviteExpiryOptions.map((days) => ({ value: String(days), label: `${days} day${days === 1 ? '' : 's'}` })), { value: 'custom', label: 'Custom date' }]} />
|
||||
{inviteExpiryMode === 'custom' ? (
|
||||
<input type="datetime-local" value={inviteCustomExpiry} onChange={(event) => setInviteCustomExpiry(event.target.value)} className="rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none transition focus:border-sky-300/35 focus:bg-white/[0.06]" />
|
||||
<DateTimePicker value={inviteCustomExpiry} onChange={setInviteCustomExpiry} placeholder="Custom expiry" clearable className="bg-white/[0.04]" />
|
||||
) : (
|
||||
<p className="px-1 text-xs text-slate-400">Leave this on default to use the global expiry window for collaborator invites.</p>
|
||||
)}
|
||||
|
||||
@@ -3,6 +3,7 @@ import { Head, usePage } from '@inertiajs/react'
|
||||
import CollectionCard from '../../components/profile/collections/CollectionCard'
|
||||
import ShareToast from '../../components/ui/ShareToast'
|
||||
import Checkbox from '../../components/ui/Checkbox'
|
||||
import DateTimePicker from '../../components/ui/DateTimePicker'
|
||||
import NovaSelect from '../../components/ui/NovaSelect'
|
||||
|
||||
function getCsrfToken() {
|
||||
@@ -634,8 +635,8 @@ export default function CollectionStaffProgramming() {
|
||||
<Field label="Priority">
|
||||
<input type="number" min="-100" max="100" value={assignmentForm.priority} onChange={(event) => setAssignmentForm((current) => ({ ...current, priority: event.target.value }))} className="w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none" />
|
||||
</Field>
|
||||
<Field label="Starts At"><input type="datetime-local" value={assignmentForm.starts_at} onChange={(event) => setAssignmentForm((current) => ({ ...current, starts_at: event.target.value }))} className="w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none" /></Field>
|
||||
<Field label="Ends At"><input type="datetime-local" value={assignmentForm.ends_at} onChange={(event) => setAssignmentForm((current) => ({ ...current, ends_at: event.target.value }))} className="w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none" /></Field>
|
||||
<Field label="Starts At"><DateTimePicker value={assignmentForm.starts_at} onChange={(nextValue) => setAssignmentForm((current) => ({ ...current, starts_at: nextValue }))} placeholder="Start time" clearable className="bg-white/[0.04]" /></Field>
|
||||
<Field label="Ends At"><DateTimePicker value={assignmentForm.ends_at} onChange={(nextValue) => setAssignmentForm((current) => ({ ...current, ends_at: nextValue }))} placeholder="End time" clearable className="bg-white/[0.04]" /></Field>
|
||||
</div>
|
||||
<Field label="Notes" help="Operational note for launch timing, overrides, or review context."><textarea value={assignmentForm.notes} onChange={(event) => setAssignmentForm((current) => ({ ...current, notes: event.target.value }))} className="mt-4 min-h-[120px] w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none" maxLength={1000} /></Field>
|
||||
<div className="mt-5 flex flex-wrap gap-3">
|
||||
|
||||
@@ -2,6 +2,7 @@ import React from 'react'
|
||||
import { Head, usePage } from '@inertiajs/react'
|
||||
import CollectionCard from '../../components/profile/collections/CollectionCard'
|
||||
import Checkbox from '../../components/ui/Checkbox'
|
||||
import DateTimePicker from '../../components/ui/DateTimePicker'
|
||||
import NovaSelect from '../../components/ui/NovaSelect'
|
||||
|
||||
function getCsrfToken() {
|
||||
@@ -406,8 +407,8 @@ export default function CollectionStaffSurfaces() {
|
||||
<Field label="Mode"><NovaSelect value={definitionForm.mode} onChange={(val) => setDefinitionForm((current) => ({ ...current, mode: val }))} searchable={false} options={[{ value: 'manual', label: 'Manual' }, { value: 'automatic', label: 'Automatic' }, { value: 'hybrid', label: 'Hybrid' }]} /></Field>
|
||||
<Field label="Ranking"><NovaSelect value={definitionForm.ranking_mode} onChange={(val) => setDefinitionForm((current) => ({ ...current, ranking_mode: val }))} searchable={false} options={[{ value: 'ranking_score', label: 'Ranking score' }, { value: 'recent_activity', label: 'Recent activity' }, { value: 'quality_score', label: 'Quality score' }]} /></Field>
|
||||
<Field label="Max Items"><input type="number" min="1" max="24" value={definitionForm.max_items} onChange={(event) => setDefinitionForm((current) => ({ ...current, max_items: event.target.value }))} className="w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none" /></Field>
|
||||
<Field label="Starts At" help="Optional activation window for the full surface definition."><input type="datetime-local" value={definitionForm.starts_at} onChange={(event) => setDefinitionForm((current) => ({ ...current, starts_at: event.target.value }))} className="w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none" /></Field>
|
||||
<Field label="Ends At" help="Leave blank when the surface should stay live until staff changes it."><input type="datetime-local" value={definitionForm.ends_at} onChange={(event) => setDefinitionForm((current) => ({ ...current, ends_at: event.target.value }))} className="w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none" /></Field>
|
||||
<Field label="Starts At" help="Optional activation window for the full surface definition."><DateTimePicker value={definitionForm.starts_at} onChange={(nextValue) => setDefinitionForm((current) => ({ ...current, starts_at: nextValue }))} placeholder="Start time" clearable className="bg-white/[0.04]" /></Field>
|
||||
<Field label="Ends At" help="Leave blank when the surface should stay live until staff changes it."><DateTimePicker value={definitionForm.ends_at} onChange={(nextValue) => setDefinitionForm((current) => ({ ...current, ends_at: nextValue }))} placeholder="End time" clearable className="bg-white/[0.04]" /></Field>
|
||||
<Field label="Fallback Surface Key" help="Optional fallback when this definition is inactive, scheduled out, or resolves no items."><input value={definitionForm.fallback_surface_key} onChange={(event) => setDefinitionForm((current) => ({ ...current, fallback_surface_key: event.target.value }))} className="w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none" maxLength={120} /></Field>
|
||||
<div className="flex items-center gap-3 rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-sm text-white"><Checkbox checked={definitionForm.is_active} onChange={(event) => setDefinitionForm((current) => ({ ...current, is_active: event.target.checked }))} label="Active" /></div>
|
||||
</div>
|
||||
@@ -429,8 +430,8 @@ export default function CollectionStaffSurfaces() {
|
||||
<Field label="Collection"><NovaSelect value={String(placementForm.collection_id || '')} onChange={(val) => setPlacementForm((current) => ({ ...current, collection_id: val }))} options={collectionOptions.map((o) => ({ value: String(o.id), label: o.title }))} /></Field>
|
||||
<Field label="Placement Type"><NovaSelect value={placementForm.placement_type} onChange={(val) => setPlacementForm((current) => ({ ...current, placement_type: val }))} searchable={false} options={[{ value: 'manual', label: 'Manual' }, { value: 'campaign', label: 'Campaign' }, { value: 'scheduled_override', label: 'Scheduled override' }]} /></Field>
|
||||
<Field label="Priority"><input type="number" min="-100" max="100" value={placementForm.priority} onChange={(event) => setPlacementForm((current) => ({ ...current, priority: event.target.value }))} className="w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none" /></Field>
|
||||
<Field label="Starts At"><input type="datetime-local" value={placementForm.starts_at} onChange={(event) => setPlacementForm((current) => ({ ...current, starts_at: event.target.value }))} className="w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none" /></Field>
|
||||
<Field label="Ends At"><input type="datetime-local" value={placementForm.ends_at} onChange={(event) => setPlacementForm((current) => ({ ...current, ends_at: event.target.value }))} className="w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none" /></Field>
|
||||
<Field label="Starts At"><DateTimePicker value={placementForm.starts_at} onChange={(nextValue) => setPlacementForm((current) => ({ ...current, starts_at: nextValue }))} placeholder="Start time" clearable className="bg-white/[0.04]" /></Field>
|
||||
<Field label="Ends At"><DateTimePicker value={placementForm.ends_at} onChange={(nextValue) => setPlacementForm((current) => ({ ...current, ends_at: nextValue }))} placeholder="End time" clearable className="bg-white/[0.04]" /></Field>
|
||||
<Field label="Campaign Key" help="Optional campaign label for reporting and grouped overrides."><input value={placementForm.campaign_key} onChange={(event) => setPlacementForm((current) => ({ ...current, campaign_key: event.target.value }))} className="w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none" maxLength={80} /></Field>
|
||||
<div className="flex items-center gap-3 rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-sm text-white"><Checkbox checked={placementForm.is_active} onChange={(event) => setPlacementForm((current) => ({ ...current, is_active: event.target.checked }))} label="Active placement" /></div>
|
||||
</div>
|
||||
@@ -493,8 +494,8 @@ export default function CollectionStaffSurfaces() {
|
||||
<Field label="Placement Type"><NovaSelect value={batchForm.placement_type} onChange={(val) => setBatchForm((current) => ({ ...current, placement_type: val }))} searchable={false} options={[{ value: 'campaign', label: 'Campaign' }, { value: 'manual', label: 'Manual' }, { value: 'scheduled_override', label: 'Scheduled override' }]} /></Field>
|
||||
<Field label="Priority"><input type="number" min="-100" max="100" value={batchForm.priority} onChange={(event) => setBatchForm((current) => ({ ...current, priority: event.target.value }))} className="w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none" /></Field>
|
||||
<div className="flex items-center gap-3 rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-sm text-white"><Checkbox checked={batchForm.is_active} onChange={(event) => setBatchForm((current) => ({ ...current, is_active: event.target.checked }))} label="Active placement" /></div>
|
||||
<Field label="Starts At"><input type="datetime-local" value={batchForm.starts_at} onChange={(event) => setBatchForm((current) => ({ ...current, starts_at: event.target.value }))} className="w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none" /></Field>
|
||||
<Field label="Ends At"><input type="datetime-local" value={batchForm.ends_at} onChange={(event) => setBatchForm((current) => ({ ...current, ends_at: event.target.value }))} className="w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none" /></Field>
|
||||
<Field label="Starts At"><DateTimePicker value={batchForm.starts_at} onChange={(nextValue) => setBatchForm((current) => ({ ...current, starts_at: nextValue }))} placeholder="Start time" clearable className="bg-white/[0.04]" /></Field>
|
||||
<Field label="Ends At"><DateTimePicker value={batchForm.ends_at} onChange={(nextValue) => setBatchForm((current) => ({ ...current, ends_at: nextValue }))} placeholder="End time" clearable className="bg-white/[0.04]" /></Field>
|
||||
</div>
|
||||
<Field label="Placement Notes"><textarea value={batchForm.notes} onChange={(event) => setBatchForm((current) => ({ ...current, notes: event.target.value }))} className="mt-4 min-h-[110px] w-full rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-3 text-white outline-none" maxLength={1000} /></Field>
|
||||
<div className="mt-5 flex flex-wrap gap-3">
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import React from 'react'
|
||||
import { Head, usePage } from '@inertiajs/react'
|
||||
import Checkbox from '../../components/ui/Checkbox'
|
||||
import DateTimePicker from '../../components/ui/DateTimePicker'
|
||||
import NovaSelect from '../../components/ui/NovaSelect'
|
||||
|
||||
function getCsrfToken() {
|
||||
@@ -576,21 +577,11 @@ export default function FeaturedArtworksAdmin() {
|
||||
</Field>
|
||||
|
||||
<Field label="Featured Since">
|
||||
<input
|
||||
type="datetime-local"
|
||||
value={form.featured_at}
|
||||
onChange={(event) => setForm((current) => ({ ...current, featured_at: event.target.value }))}
|
||||
className="w-full rounded-2xl border border-white/10 bg-[#08111d] px-4 py-3 text-sm text-white outline-none transition focus:border-sky-300/40"
|
||||
/>
|
||||
<DateTimePicker value={form.featured_at} onChange={(nextValue) => setForm((current) => ({ ...current, featured_at: nextValue }))} placeholder="Featured since" clearable className="bg-[#08111d]" />
|
||||
</Field>
|
||||
|
||||
<Field label="Expires">
|
||||
<input
|
||||
type="datetime-local"
|
||||
value={form.expires_at}
|
||||
onChange={(event) => setForm((current) => ({ ...current, expires_at: event.target.value }))}
|
||||
className="w-full rounded-2xl border border-white/10 bg-[#08111d] px-4 py-3 text-sm text-white outline-none transition focus:border-sky-300/40"
|
||||
/>
|
||||
<DateTimePicker value={form.expires_at} onChange={(nextValue) => setForm((current) => ({ ...current, expires_at: nextValue }))} placeholder="Expiry date" clearable className="bg-[#08111d]" />
|
||||
</Field>
|
||||
|
||||
<div className="sm:col-span-2 flex flex-wrap gap-3">
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import React from 'react'
|
||||
import { Head, Link, usePage } from '@inertiajs/react'
|
||||
import Checkbox from '../../components/ui/Checkbox'
|
||||
import DateTimePicker from '../../components/ui/DateTimePicker'
|
||||
import NovaSelect from '../../components/ui/NovaSelect'
|
||||
|
||||
function requestJson(url, { method = 'GET', body } = {}) {
|
||||
@@ -115,14 +116,14 @@ export default function NovaCardsChallengeAdmin() {
|
||||
<span className="mb-2 block">Winner card</span>
|
||||
<NovaSelect value={String(form.winner_card_id || '')} onChange={(val) => setForm((current) => ({ ...current, winner_card_id: val }))} placeholder="No winner" options={cards.map((c) => ({ value: String(c.id), label: c.title }))} />
|
||||
</div>
|
||||
<label className="text-sm text-slate-300">
|
||||
<div className="text-sm text-slate-300">
|
||||
<span className="mb-2 block">Starts at</span>
|
||||
<input type="datetime-local" value={form.starts_at} onChange={(event) => setForm((current) => ({ ...current, starts_at: event.target.value }))} className="w-full rounded-2xl border border-white/10 bg-[#0d1726] px-4 py-3 text-white" />
|
||||
</label>
|
||||
<label className="text-sm text-slate-300">
|
||||
<DateTimePicker value={form.starts_at} onChange={(nextValue) => setForm((current) => ({ ...current, starts_at: nextValue }))} placeholder="Starts at" clearable className="bg-[#0d1726]" />
|
||||
</div>
|
||||
<div className="text-sm text-slate-300">
|
||||
<span className="mb-2 block">Ends at</span>
|
||||
<input type="datetime-local" value={form.ends_at} onChange={(event) => setForm((current) => ({ ...current, ends_at: event.target.value }))} className="w-full rounded-2xl border border-white/10 bg-[#0d1726] px-4 py-3 text-white" />
|
||||
</label>
|
||||
<DateTimePicker value={form.ends_at} onChange={(nextValue) => setForm((current) => ({ ...current, ends_at: nextValue }))} placeholder="Ends at" clearable className="bg-[#0d1726]" />
|
||||
</div>
|
||||
<textarea value={JSON.stringify(form.rules_json || {}, null, 2)} onChange={(event) => {
|
||||
try {
|
||||
setForm((current) => ({ ...current, rules_json: JSON.parse(event.target.value || '{}') }))
|
||||
|
||||
Reference in New Issue
Block a user