Skip to content

Commit

Permalink
new version
Browse files Browse the repository at this point in the history
  • Loading branch information
Javid Momeni committed Dec 7, 2024
1 parent af6902c commit 3ceb69a
Show file tree
Hide file tree
Showing 29 changed files with 1,137 additions and 1,118 deletions.
12 changes: 9 additions & 3 deletions app/globals.css
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
@import url('https://lib.arvancloud.ir/vazir-font/33.003/UI-Farsi-Digits/Vazirmatn-UI-FD-font-face.css');

@tailwind base;
@tailwind components;
@tailwind utilities;
Expand Down Expand Up @@ -44,6 +46,7 @@
--chart-5: 27 87% 67%;
--radius: 0.5rem;
}

.dark {
--background: 0 0% 3.9%;
--foreground: 0 0% 98%;
Expand All @@ -70,13 +73,16 @@
--chart-4: 280 65% 60%;
--chart-5: 340 75% 55%;
}
}

@layer base {
html {
font-family: 'Vazirmatn', sans-serif;
}

* {
@apply border-border;
}

body {
@apply bg-background text-foreground;
}
}
}
6 changes: 3 additions & 3 deletions app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ const vazirmatn = Vazirmatn({
});

export const metadata: Metadata = {
title: 'قصه‌پرداز: سفر به دنیای داستان‌های جادویی برای کودکان',
description: 'جرقه‌ای برای تخیل کودک شما، با هر داستانی که هوش مصنوعی خلق می‌کند',
title: 'AiNovelist - AI-Powered Stories for Kids',
description: 'Create magical stories for children with the power of AI',
};

export default function RootLayout({
Expand All @@ -20,7 +20,7 @@ export default function RootLayout({
children: React.ReactNode;
}) {
return (
<html lang="fa" dir='rtl' suppressHydrationWarning>
<html lang="fa" dir="rtl" suppressHydrationWarning>
<body className={`${vazirmatn.variable} font-sans`} suppressHydrationWarning>
<div className="flex min-h-screen flex-col">
<Header />
Expand Down
21 changes: 21 additions & 0 deletions app/library/[id]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { fetchStories } from '@/lib/api';
import { StoryDetail } from '@/components/library/story-detail';
import { notFound } from 'next/navigation';

export async function generateStaticParams() {
const stories = await fetchStories();
return stories.map((story) => ({
id: story.id,
}));
}

export default async function StoryPage({ params }: { params: { id: string } }) {
const stories = await fetchStories();
const story = stories.find((s) => s.id === params.id);

if (!story) {
notFound();
}

return <StoryDetail story={story} />;
}
7 changes: 5 additions & 2 deletions app/library/page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import { fetchStories } from '@/lib/api';
import { StoryLibrary } from '@/components/library/story-library';

export default function LibraryPage() {
return <StoryLibrary />;
export default async function LibraryPage() {
const stories = await fetchStories();

return <StoryLibrary initialStories={stories} />;
}
241 changes: 25 additions & 216 deletions components/create/story-creator.tsx
Original file line number Diff line number Diff line change
@@ -1,239 +1,48 @@
"use client";

import { useState } from "react";
import { Button } from "@/components/ui/button";
import { Card, CardHeader, CardTitle } from "@/components/ui/card";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Textarea } from "@/components/ui/textarea";
import { Slider } from "@/components/ui/slider";
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
import { StoryForm } from "@/lib/types";
import { StoryPreview } from "@/components/create/story-preview";

const environmentalTopicsMap: Record<string, string> = {
"حفاظت از حیوانات": "Animal Protection",
"کاهش زباله": "Waste Reduction",
"صرفه‌جویی در آب": "Water Conservation",
"حفاظت از درختان": "Tree Preservation",
"کاهش آلودگی هوا": "Air Pollution Reduction",
};

const livingEnvironmentsMap: Record<string, string> = {
شهر: "City",
روستا: "Village",
"حومه شهر": "Suburbs",
ساحل: "Coast",
کوهستان: "Mountainous Area",
};

const transformFormData = (formData: StoryForm) => {
return {
title: formData.title,
sex: formData.sex,
ageRange: formData.ageRange[0],
theme: formData.theme,
characters: formData.characters,
additionalNotes: formData.additionalNotes,
environmentalTopic:
environmentalTopicsMap[formData.environmentalTopic] || "",
livingEnvironment: livingEnvironmentsMap[formData.livingEnvironment] || "",
};
};
import { useState } from 'react';
import { Button } from '@/components/ui/button';
import { Card } from '@/components/ui/card';
import { StoryForm } from '@/lib/types';
import { StoryPreview } from '@/components/create/story-preview';
import { StoryFormFields } from '@/components/create/story-form-fields';

export function StoryCreator() {
const [apiResponse, setApiResponse] = useState<{
aiResponse: string;
approach: string[];
git: string | null;
success: boolean;
} | null>(null);
const [showPreview, setShowPreview] = useState(true);

const [form, setForm] = useState<StoryForm>({
title: "",
sex: "male",
title: '',
ageRange: [6],
theme: "",
characters: "",
additionalNotes: "",
environmentalTopic: "",
livingEnvironment: "",
theme: '',
characters: '',
additionalNotes: '',
childGender: 'boy',
academicApproaches: [],
topic: '',
livingArea: '',
});
const [isGenerating, setIsGenerating] = useState(false);
const [response, setResponse] = useState<string | null>(null);

const handleInputChange = (
field: keyof StoryForm,
value: string | number[] | string
) => {
setForm((prevForm) => ({
...prevForm,
[field]: value,
}));
};
const [isGenerating, setIsGenerating] = useState(false);

const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
const transformedValues = transformFormData(form);
setIsGenerating(true);

try {
const response = await fetch(
"https://aibots.kharcoin.info/ai-story/build",
{
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(transformedValues),
}
);

const data = await response.json();
setApiResponse(data);
setShowPreview(false);
setResponse(data);
setForm({
title: "",
sex: "male",
ageRange: [6],
theme: "",
characters: "",
additionalNotes: "",
environmentalTopic: "",
livingEnvironment: "",
});
console.log(data);
} catch (error) {
console.error("Error:", error);
setResponse("متاسفانه در ارسال فرم خطایی رخ داد");
} finally {
setIsGenerating(false);
}
// TODO: Implement story generation logic
setTimeout(() => setIsGenerating(false), 2000);
};

return (
<div className="container mx-auto px-4 py-8">
<h1 className="mb-8 text-3xl font-bold">قصه‌تان را بسازید</h1>
<h1 className="mb-8 text-3xl font-bold">Create Your Story</h1>
<div className="grid gap-8 md:grid-cols-2">
<Card className="p-6">
<form onSubmit={handleSubmit} className="space-y-6">
<div className="space-y-2">
<Label htmlFor="title">قصه</Label>
<Input
id="title"
value={form.title}
onChange={(e) => handleInputChange("title", e.target.value)}
placeholder="قصه شما چه اسمی داشته باشد؟"
/>
</div>
<div className="space-y-2">
<Label htmlFor="characters">شخصیت اصلی</Label>
<Input
id="characters"
value={form.characters}
onChange={(e) =>
handleInputChange("characters", e.target.value)
}
placeholder="Describe your characters"
/>
</div>
<div className="space-y-2">
<RadioGroup
label="جنسیت"
value={form.sex}
onValueChange={(value) => handleInputChange("sex", value)}
>
<RadioGroupItem value="male">پسر</RadioGroupItem>
<RadioGroupItem value="female">دختر</RadioGroupItem>
</RadioGroup>
</div>

<div className="space-y-2">
<Label htmlFor="age-range">سن: {form.ageRange[0]} ساله</Label>
<Slider
id="age-range"
value={form.ageRange}
onValueChange={(value) => handleInputChange("ageRange", value)}
min={3}
max={11}
step={1}
/>
</div>
<div className="grid grid-cols-2 gap-4">
<div className="space-y-2">
<Label htmlFor="environmental-topic">موضوع زیست محیطی</Label>
<select
id="environmental-topic"
value={form.environmentalTopic}
onChange={(e) =>
handleInputChange("environmentalTopic", e.target.value)
}
className="rounded-md border border-gray-300 px-4 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500"
>
<option value="">انتخاب کنید</option>
{Object.keys(environmentalTopicsMap).map((topic) => (
<option key={topic} value={topic}>
{topic}
</option>
))}
</select>
</div>
<div className="space-y-2">
<Label htmlFor="living-environment">محیط زندگی</Label>
<select
id="living-environment"
value={form.livingEnvironment}
onChange={(e) =>
handleInputChange("livingEnvironment", e.target.value)
}
className="rounded-md border border-gray-300 px-4 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500"
>
<option value="">انتخاب کنید</option>
{Object.keys(livingEnvironmentsMap).map((environment) => (
<option key={environment} value={environment}>
{environment}
</option>
))}
</select>
</div>
</div>

<div className="space-y-2">
<Label htmlFor="additional-notes">نکات تکمیلی</Label>
<Textarea
id="additional-notes"
value={form.additionalNotes}
onChange={(e) =>
handleInputChange("additionalNotes", e.target.value)
}
placeholder="Any special requests or additional details"
/>
</div>
<Button type="submit" className="w-full" disabled={isGenerating}>
{isGenerating ? "در حال ارسال..." : "شروع به قصه گویی"}
<form onSubmit={handleSubmit}>
<StoryFormFields form={form} setForm={setForm} />
<Button type="submit" className="w-full mt-6" disabled={isGenerating}>
{isGenerating ? 'Creating Story...' : 'Generate Story'}
</Button>
</form>
</Card>
{showPreview ? (
<StoryPreview
form={form}
environmentalTopicsMap={environmentalTopicsMap}
livingEnvironmentsMap={livingEnvironmentsMap}
/>
) : (
<Card className="p-6">
<CardHeader>
</CardHeader>
{apiResponse && apiResponse.aiResponse ? (
<div className="space-y-4">{apiResponse.aiResponse}</div>
) : (
<div className="space-y-4">No response available</div>
)}
</Card>
)}
</div>
<StoryPreview form={form} />
</div>
</div>
);
}
}
Loading

0 comments on commit 3ceb69a

Please sign in to comment.