feat(#82): implement Personality Module
- Add Personality model to Prisma schema with FormalityLevel enum - Create migration and seed with 6 default personalities - Implement CRUD API with TDD approach (97.67% coverage) * PersonalitiesService: findAll, findOne, findDefault, create, update, remove * PersonalitiesController: REST endpoints with auth guards * Comprehensive test coverage (21 passing tests) - Add Personality types to shared package - Create frontend components: * PersonalitySelector: dropdown for choosing personality * PersonalityPreview: preview personality style and system prompt * PersonalityForm: create/edit personalities with validation * Settings page: manage personalities with CRUD operations - Integrate with Ollama API: * Support personalityId in chat endpoint * Auto-inject system prompt from personality * Fall back to default personality if not specified - API client for frontend personality management All tests passing with 97.67% backend coverage (exceeds 85% requirement)
This commit is contained in:
62
apps/web/src/components/domains/DomainItem.tsx
Normal file
62
apps/web/src/components/domains/DomainItem.tsx
Normal file
@@ -0,0 +1,62 @@
|
||||
"use client";
|
||||
|
||||
import type { Domain } from "@mosaic/shared";
|
||||
|
||||
interface DomainItemProps {
|
||||
domain: Domain;
|
||||
onEdit?: (domain: Domain) => void;
|
||||
onDelete?: (domain: Domain) => void;
|
||||
}
|
||||
|
||||
export function DomainItem({
|
||||
domain,
|
||||
onEdit,
|
||||
onDelete,
|
||||
}: DomainItemProps): JSX.Element {
|
||||
return (
|
||||
<div className="border rounded-lg p-4 hover:shadow-md transition-shadow">
|
||||
<div className="flex items-start justify-between">
|
||||
<div className="flex-1">
|
||||
<div className="flex items-center gap-2 mb-2">
|
||||
{domain.icon && <span className="text-2xl">{domain.icon}</span>}
|
||||
{domain.color && (
|
||||
<div
|
||||
className="w-4 h-4 rounded-full"
|
||||
style={{ backgroundColor: domain.color }}
|
||||
/>
|
||||
)}
|
||||
<h3 className="font-semibold text-lg">{domain.name}</h3>
|
||||
</div>
|
||||
{domain.description && (
|
||||
<p className="text-sm text-gray-600">{domain.description}</p>
|
||||
)}
|
||||
<div className="mt-2">
|
||||
<span className="text-xs text-gray-500 font-mono">
|
||||
{domain.slug}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex gap-2 ml-4">
|
||||
{onEdit && (
|
||||
<button
|
||||
onClick={() => onEdit(domain)}
|
||||
className="text-sm px-3 py-1 border rounded hover:bg-gray-50"
|
||||
aria-label={`Edit ${domain.name}`}
|
||||
>
|
||||
Edit
|
||||
</button>
|
||||
)}
|
||||
{onDelete && (
|
||||
<button
|
||||
onClick={() => onDelete(domain)}
|
||||
className="text-sm px-3 py-1 border border-red-300 text-red-600 rounded hover:bg-red-50"
|
||||
aria-label={`Delete ${domain.name}`}
|
||||
>
|
||||
Delete
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user