import { clsx } from "@akanjs/client";
import { ModelType } from "@your-app/client";
interface ComponentProps {
className?: string;
// Feature-specific props
}
export const Component = ({ className, ...props }: ComponentProps) => {
return <div className={clsx("base-classes", className)}>
{/* Component content */}
</div>;
};
{apps,libs}/
└── project-name/
└── lib/
└── feature-name/
├── FeatureName.Unit.tsx
├── FeatureName.View.tsx
├── FeatureName.Template.tsx
├── FeatureName.Util.tsx
├── FeatureName.Zone.tsx
├── featureName.constant.ts
├── featureName.service.ts
├── featureName.signal.ts
├── featureName.store.ts
└── index.ts
export const Card = ({ model, href }) => {
return (
<Link href={href}
className="card bg-base-100 rounded-lg shadow-sm transition-shadow hover:shadow-lg">
<div className="card-body p-4">
<h3 className="card-title font-medium">{model.title}</h3>
<p className="text-base-content/70">{model.description}</p>
</div>
</Link>
);
};
export const General = ({ model, className }) => {
return (
<div className={clsx("bg-base-100 rounded-lg p-6 shadow-lg", className)}>
<div className="border-primary mb-6 border-l-4 pl-4">
<h1 className="text-primary text-2xl font-bold">{model.title}</h1>
</div>
<div className="grid grid-cols-1 gap-6 md:grid-cols-2">
{/* Model details */}
</div>
</div>
);
};
export const General = () => {
const form = useModelForm();
return (
<div className="space-y-4">
<div className="form-control w-full">
<label className="label">
<span className="label-text">Title</span>
</label>
<input
value={form.title ?? ""}
onChange={(e) => setTitle(e.target.value)}
className="input input-bordered w-full"
/>
</div>
{/* Additional form fields */}
</div>
);
};
export const Toolbar = ({ model, className }) => {
return (
<div className={clsx("flex gap-2", className)}>
<button className="btn btn-primary btn-sm">Duplicate</button>
<button className="btn btn-error btn-sm">Delete</button>
</div>
);
};
export const List = ({ init, query, className }) => {
return (
<Load.Units
init={init}
query={query}
className={clsx("grid grid-cols-1 gap-4 md:grid-cols-2", className)}
renderItem={(model) => <Model.Unit.Card model={model} />}
renderEmpty={() => <div className="alert alert-info">No items found</div>}
/>
);
};
Option | Type | Default | Description | Example |
---|---|---|---|---|
{Model}.Unit.tsx | Pattern | - | Card/list item components for model display |
|
{Model}.View.tsx | Pattern | - | Detailed view components for single models |
|
{Model}.Template.tsx | Pattern | - | Form/edit components for model manipulation |
|
{Model}.Util.tsx | Pattern | - | Utility components specific to a feature |
|
{Model}.Zone.tsx | Pattern | - | Container components with data loading |
|
{model}.constant.ts | Pattern | - | Types, GraphQL schema, and enums |
|
{model}.service.ts | Pattern | - | Business logic and database operations |
|
{model}.signal.ts | Pattern | - | Client state management and API calls |
|
Card/list item components for model display
Detailed view components for single models
Form/edit components for model manipulation
Utility components specific to a feature
Container components with data loading
Types, GraphQL schema, and enums
Business logic and database operations
Client state management and API calls
utility | Description | Example |
---|---|---|
Cookie Management | Handles browser cookie operations |
|
Storage Utilities | Cross-platform storage for web and mobile |
|
Device Capabilities | Access device features like haptic feedback |
|
Routing | Navigation and URL management |
|
CSS Utilities | Class name management and composition |
|
Handles browser cookie operations
setCookie("session", "token123");
const session = getCookie("session");
Cross-platform storage for web and mobile
await storage.setItem("key", "value");
const value = await storage.getItem("key");
Access device features like haptic feedback
await device.init();
device.vibrate("medium");
Navigation and URL management
router.push("/dashboard");
router.replace("/login", { q: "term" });
Class name management and composition
clsx("base-class", isActive && "active-class", className)
export const ProductCard = ({ product, href }) => {
return (
<Link href={href} className="card bg-base-100 rounded-lg shadow-sm transition-shadow hover:shadow-lg">
<figure className="px-4 pt-4">
<img src={product.image?.url} alt={product.name}
className="h-48 w-full rounded-lg object-cover" />
</figure>
<div className="card-body p-4">
<h3 className="card-title font-medium">{product.name}</h3>
<div className="text-primary">${product.price?.toFixed(2)}</div>
<p className="text-base-content/70 text-sm">{product.description}</p>
</div>
</Link>
);
};
export const ProductView = ({ product, className }) => {
return (
<div className={clsx("bg-base-100 rounded-lg p-6 shadow-lg", className)}>
<div className="flex flex-col gap-6 md:flex-row">
<div className="w-full md:w-1/2">
<img src={product.image?.url} alt={product.name}
className="w-full rounded-lg object-cover" />
</div>
<div className="w-full md:w-1/2 space-y-4">
<h1 className="text-3xl font-bold">{product.name}</h1>
<div className="text-primary text-xl font-medium">
${product.price?.toFixed(2)}
</div>
<p className="whitespace-pre-wrap">{product.description}</p>
<div className="flex gap-2">
<button className="btn btn-primary">Add to Cart</button>
<button className="btn btn-outline">Save for Later</button>
</div>
</div>
</div>
</div>
);
};
export const ProductForm = () => {
const [form, setForm] = useState({ name: '', price: 0 });
return (
<div className="space-y-6">
<div className="form-control w-full">
<label className="label">
<span className="label-text">Product Name</span>
</label>
<input
value={form.name}
onChange={(e) => setForm({...form, name: e.target.value})}
className="input input-bordered w-full"
/>
</div>
<div className="form-control w-full">
<label className="label">
<span className="label-text">Price</span>
</label>
<input
type="number"
value={form.price}
onChange={(e) => setForm({...form, price: Number(e.target.value)})}
className="input input-bordered w-full"
/>
</div>
</div>
);
};