Update carousels, card hover, and IranBay branding.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Alireza Hassani
2026-10-04 13:21:27 +03:30
co-authored by Cursor
commit 1153e8e78f
178 changed files with 30517 additions and 0 deletions
+129
View File
@@ -0,0 +1,129 @@
"use client";
import { useEffect, useRef, useState } from "react";
import styles from "./CategoryTreeView.module.css";
export type CategoryTreeNode = {
id: string;
name: string;
hint?: string;
children: CategoryTreeNode[];
};
export type CategoryTreeActions = {
onAddChild?: (id: string) => void;
onEdit?: (id: string) => void;
onRemove?: (id: string) => void;
};
export default function CategoryTreeView({
nodes,
emptyLabel = "No categories yet",
actions,
}: {
nodes: CategoryTreeNode[];
emptyLabel?: string;
actions?: CategoryTreeActions;
}) {
if (!nodes.length) {
return <p className={styles.empty}>{emptyLabel}</p>;
}
return (
<ul className={styles.list}>
{nodes.map((node) => (
<TreeBranch key={node.id} node={node} depth={0} actions={actions} />
))}
</ul>
);
}
function TreeBranch({
node,
depth,
actions,
}: {
node: CategoryTreeNode;
depth: number;
actions?: CategoryTreeActions;
}) {
const [open, setOpen] = useState(false);
const childCount = node.children.length;
const previousChildCount = useRef(childCount);
const hasChildren = childCount > 0;
const hasActions = Boolean(
actions?.onAddChild || actions?.onEdit || actions?.onRemove,
);
useEffect(() => {
if (childCount > previousChildCount.current) {
setOpen(true);
}
previousChildCount.current = childCount;
}, [childCount]);
return (
<li className={styles.node}>
<div
className={styles.row}
style={{ paddingInlineStart: `${12 + depth * 56}px` }}
>
{hasChildren ? (
<button
type="button"
className={`${styles.expand} ${open ? styles.expandOpen : ""}`}
aria-expanded={open}
aria-label={open ? "Collapse" : "Expand"}
onClick={() => setOpen((value) => !value)}
>
<span aria-hidden>{open ? "−" : "+"}</span>
</button>
) : (
<span className={styles.expandSpacer} aria-hidden />
)}
<span className={styles.name} dir="auto">
{node.name}
</span>
{node.hint ? <span className={styles.hint}>{node.hint}</span> : null}
{hasActions ? (
<div className={styles.actions}>
{actions?.onAddChild ? (
<button
type="button"
onClick={() => actions.onAddChild?.(node.id)}
>
Add sub
</button>
) : null}
{actions?.onEdit ? (
<button type="button" onClick={() => actions.onEdit?.(node.id)}>
Edit
</button>
) : null}
{actions?.onRemove ? (
<button
type="button"
className={styles.danger}
onClick={() => actions.onRemove?.(node.id)}
>
Remove
</button>
) : null}
</div>
) : null}
</div>
{hasChildren && open ? (
<ul className={styles.list}>
{node.children.map((child) => (
<TreeBranch
key={child.id}
node={child}
depth={depth + 1}
actions={actions}
/>
))}
</ul>
) : null}
</li>
);
}