Add Super Admin AI prompt templates and business assignment UI.

Templates library plus per-business prompt list with edit, copy public link, and remove.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Alireza Hassani
2026-08-26 15:28:41 +03:30
co-authored by Cursor
parent b50e4fd812
commit 0aab7b5529
13 changed files with 1209 additions and 132 deletions
+174 -127
View File
@@ -1,4 +1,5 @@
import { useEffect, useId, useMemo, useRef, useState } from 'react'
import { useEffect, useId, useLayoutEffect, useMemo, useRef, useState } from 'react'
import { createPortal } from 'react-dom'
import { CalendarDays, ChevronLeft } from 'lucide-react'
import styles from './StepDatePicker.module.css'
@@ -12,6 +13,8 @@ interface StepDatePickerProps {
placeholder?: string
}
const PANEL_WIDTH = 300
const MONTHS = [
'Jan',
'Feb',
@@ -81,8 +84,12 @@ export function StepDatePicker({
const autoId = useId()
const fieldId = id ?? autoId
const rootRef = useRef<HTMLDivElement>(null)
const panelRef = useRef<HTMLDivElement>(null)
const [open, setOpen] = useState(false)
const [step, setStep] = useState<Step>('year')
const [panelPos, setPanelPos] = useState<{ top: number; left: number } | null>(
null,
)
const today = useMemo(() => {
const now = new Date()
@@ -103,12 +110,41 @@ export function StepDatePicker({
return Array.from({ length: end - start + 1 }, (_, i) => start + i)
}, [today.year])
function updatePanelPosition() {
const trigger = rootRef.current
if (!trigger) return
const rect = trigger.getBoundingClientRect()
const width = Math.min(PANEL_WIDTH, window.innerWidth - 24)
let left = rect.right - width
if (left < 12) left = 12
if (left + width > window.innerWidth - 12) {
left = Math.max(12, window.innerWidth - 12 - width)
}
setPanelPos({ top: rect.bottom + 4, left })
}
useLayoutEffect(() => {
if (!open) {
setPanelPos(null)
return
}
updatePanelPosition()
const onReposition = () => updatePanelPosition()
window.addEventListener('resize', onReposition)
window.addEventListener('scroll', onReposition, true)
return () => {
window.removeEventListener('resize', onReposition)
window.removeEventListener('scroll', onReposition, true)
}
}, [open])
useEffect(() => {
if (!open) return
const onPointerDown = (event: MouseEvent) => {
if (!rootRef.current?.contains(event.target as Node)) {
setOpen(false)
}
const target = event.target as Node
if (rootRef.current?.contains(target)) return
if (panelRef.current?.contains(target)) return
setOpen(false)
}
const onKeyDown = (event: KeyboardEvent) => {
if (event.key === 'Escape') setOpen(false)
@@ -155,6 +191,139 @@ export function StepDatePicker({
? today.day
: null
const panel =
open && panelPos
? createPortal(
<div
ref={panelRef}
className={styles.panel}
role="dialog"
aria-label="Choose date"
style={{ top: panelPos.top, left: panelPos.left }}
>
<div className={styles.panelHeader}>
{step !== 'year' ? (
<button
type="button"
className={styles.backBtn}
onClick={() => setStep(step === 'day' ? 'month' : 'year')}
aria-label="Back"
>
<ChevronLeft size={16} />
</button>
) : (
<span className={styles.backSpacer} />
)}
<div className={styles.steps}>
<button
type="button"
className={`${styles.stepChip} ${step === 'year' ? styles.stepActive : ''}`}
onClick={() => setStep('year')}
>
{draftYear}
</button>
<button
type="button"
className={`${styles.stepChip} ${step === 'month' ? styles.stepActive : ''}`}
onClick={() => setStep('month')}
>
{MONTHS[draftMonth - 1]}
</button>
<span
className={`${styles.stepChip} ${step === 'day' ? styles.stepActive : ''} ${styles.stepStatic}`}
>
Day
</span>
</div>
</div>
{step === 'year' ? (
<div className={styles.yearList}>
{years.map((year) => (
<button
key={year}
type="button"
className={`${styles.optionBtn} ${
year === draftYear ? styles.optionSelected : ''
} ${year === today.year ? styles.optionToday : ''}`}
onClick={() => pickYear(year)}
>
{year}
</button>
))}
</div>
) : null}
{step === 'month' ? (
<div className={styles.monthGrid}>
{MONTHS.map((label, index) => {
const month = index + 1
return (
<button
key={label}
type="button"
className={`${styles.optionBtn} ${
month === draftMonth ? styles.optionSelected : ''
} ${
draftYear === today.year && month === today.month
? styles.optionToday
: ''
}`}
onClick={() => pickMonth(month)}
>
{label}
</button>
)
})}
</div>
) : null}
{step === 'day' ? (
<div className={styles.dayPanel}>
<div className={styles.weekdayRow}>
{['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'].map((d) => (
<span key={d} className={styles.weekday}>
{d}
</span>
))}
</div>
<div className={styles.dayGrid}>
{Array.from({ length: padStart }, (_, i) => (
<span key={`pad-${i}`} className={styles.dayEmpty} />
))}
{Array.from({ length: dayCount }, (_, i) => {
const day = i + 1
const isSelected =
selected?.year === draftYear &&
selected?.month === draftMonth &&
selected?.day === day
const isToday =
draftYear === today.year &&
draftMonth === today.month &&
day === today.day
return (
<button
key={day}
type="button"
className={`${styles.dayBtn} ${
isSelected ? styles.daySelected : ''
} ${isToday && !isSelected ? styles.dayToday : ''} ${
highlightDay === day && !isSelected ? styles.dayPrefill : ''
}`}
onClick={() => pickDay(day)}
>
{day}
</button>
)
})}
</div>
</div>
) : null}
</div>,
document.body,
)
: null
return (
<div className={styles.root} ref={rootRef}>
<button
@@ -171,129 +340,7 @@ export function StepDatePicker({
<span className={styles.triggerText}>{display || placeholder}</span>
<CalendarDays size={16} className={styles.icon} aria-hidden="true" />
</button>
{open ? (
<div className={styles.panel} role="dialog" aria-label="Choose date">
<div className={styles.panelHeader}>
{step !== 'year' ? (
<button
type="button"
className={styles.backBtn}
onClick={() => setStep(step === 'day' ? 'month' : 'year')}
aria-label="Back"
>
<ChevronLeft size={16} />
</button>
) : (
<span className={styles.backSpacer} />
)}
<div className={styles.steps}>
<button
type="button"
className={`${styles.stepChip} ${step === 'year' ? styles.stepActive : ''}`}
onClick={() => setStep('year')}
>
{draftYear}
</button>
<button
type="button"
className={`${styles.stepChip} ${step === 'month' ? styles.stepActive : ''}`}
onClick={() => setStep('month')}
>
{MONTHS[draftMonth - 1]}
</button>
<span
className={`${styles.stepChip} ${step === 'day' ? styles.stepActive : ''} ${styles.stepStatic}`}
>
Day
</span>
</div>
</div>
{step === 'year' ? (
<div className={styles.yearList}>
{years.map((year) => (
<button
key={year}
type="button"
className={`${styles.optionBtn} ${
year === draftYear ? styles.optionSelected : ''
} ${year === today.year ? styles.optionToday : ''}`}
onClick={() => pickYear(year)}
>
{year}
</button>
))}
</div>
) : null}
{step === 'month' ? (
<div className={styles.monthGrid}>
{MONTHS.map((label, index) => {
const month = index + 1
return (
<button
key={label}
type="button"
className={`${styles.optionBtn} ${
month === draftMonth ? styles.optionSelected : ''
} ${
draftYear === today.year && month === today.month
? styles.optionToday
: ''
}`}
onClick={() => pickMonth(month)}
>
{label}
</button>
)
})}
</div>
) : null}
{step === 'day' ? (
<div className={styles.dayPanel}>
<div className={styles.weekdayRow}>
{['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'].map((d) => (
<span key={d} className={styles.weekday}>
{d}
</span>
))}
</div>
<div className={styles.dayGrid}>
{Array.from({ length: padStart }, (_, i) => (
<span key={`pad-${i}`} className={styles.dayEmpty} />
))}
{Array.from({ length: dayCount }, (_, i) => {
const day = i + 1
const isSelected =
selected?.year === draftYear &&
selected?.month === draftMonth &&
selected?.day === day
const isToday =
draftYear === today.year &&
draftMonth === today.month &&
day === today.day
return (
<button
key={day}
type="button"
className={`${styles.dayBtn} ${
isSelected ? styles.daySelected : ''
} ${isToday && !isSelected ? styles.dayToday : ''} ${
highlightDay === day && !isSelected ? styles.dayPrefill : ''
}`}
onClick={() => pickDay(day)}
>
{day}
</button>
)
})}
</div>
</div>
) : null}
</div>
) : null}
{panel}
</div>
)
}