init: kovakemlak-crm project scaffold

- Next.js 16 + Appwrite multi-tenant emlak CRM
- Database: kovakemlak-db (properties, customers, customer_searches, property_matches, presentations, investors, activities, tenant_settings)
- Same stack as isletmem-kovakcrm (shadcn/ui template base)
- Modules: portföy, müşteri takibi, arama kriterleri, otomatik eşleştirme, sunum linki, yatırımcı portalı
This commit is contained in:
egecankomur
2026-05-05 04:37:04 +03:00
commit 37679e83e6
383 changed files with 53525 additions and 0 deletions
@@ -0,0 +1,290 @@
"use client"
import { Dices, Upload, Sun, Moon } from 'lucide-react'
import { Button } from '@/components/ui/button'
import { Label } from '@/components/ui/label'
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'
import { Separator } from '@/components/ui/separator'
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@/components/ui/accordion'
import { useThemeManager } from '@/hooks/use-theme-manager'
import { useCircularTransition } from '@/hooks/use-circular-transition'
import { colorThemes, tweakcnThemes } from '@/config/theme-data'
import { radiusOptions, baseColors } from '@/config/theme-customizer-constants'
import { ColorPicker } from '@/components/color-picker'
import type { ImportedTheme } from '@/types/theme-customizer'
import React from 'react'
import "./circular-transition.css"
interface ThemeTabProps {
selectedTheme: string
setSelectedTheme: (theme: string) => void
selectedTweakcnTheme: string
setSelectedTweakcnTheme: (theme: string) => void
selectedRadius: string
setSelectedRadius: (radius: string) => void
setImportedTheme: (theme: ImportedTheme | null) => void
onImportClick: () => void
}
export function ThemeTab({
selectedTheme,
setSelectedTheme,
selectedTweakcnTheme,
setSelectedTweakcnTheme,
selectedRadius,
setSelectedRadius,
setImportedTheme,
onImportClick
}: ThemeTabProps) {
const {
isDarkMode,
brandColorsValues,
setBrandColorsValues,
applyTheme,
applyTweakcnTheme,
applyRadius,
handleColorChange
} = useThemeManager()
const { toggleTheme } = useCircularTransition()
const handleRandomShadcn = () => {
// Apply a random shadcn theme
const randomTheme = colorThemes[Math.floor(Math.random() * colorThemes.length)]
setSelectedTheme(randomTheme.value)
setSelectedTweakcnTheme("") // Clear tweakcn selection
setBrandColorsValues({}) // Clear brand colors state
setImportedTheme(null) // Clear imported theme
applyTheme(randomTheme.value, isDarkMode)
}
const handleRandomTweakcn = () => {
// Apply a random tweakcn theme
const randomTheme = tweakcnThemes[Math.floor(Math.random() * tweakcnThemes.length)]
setSelectedTweakcnTheme(randomTheme.value)
setSelectedTheme("") // Clear shadcn selection
setBrandColorsValues({}) // Clear brand colors state
setImportedTheme(null) // Clear imported theme
applyTweakcnTheme(randomTheme.preset, isDarkMode)
}
const handleRadiusSelect = (radius: string) => {
setSelectedRadius(radius)
applyRadius(radius)
}
const handleLightMode = (event: React.MouseEvent<HTMLButtonElement>) => {
if (isDarkMode === false) return
toggleTheme(event)
}
const handleDarkMode = (event: React.MouseEvent<HTMLButtonElement>) => {
if (isDarkMode === true) return
toggleTheme(event)
}
return (
<div className="p-4 space-y-6">
{/* Hazır temalar */}
<div className="space-y-3">
<div className="flex items-center justify-between">
<Label className="text-sm font-medium">Hazır temalar</Label>
<Button variant="outline" size="sm" onClick={handleRandomShadcn} className="cursor-pointer">
<Dices className="h-3.5 w-3.5 mr-1.5" />
Rastgele
</Button>
</div>
<Select value={selectedTheme} onValueChange={(value) => {
setSelectedTheme(value)
setSelectedTweakcnTheme("") // Clear other selection
setBrandColorsValues({}) // Clear brand colors state
setImportedTheme(null) // Clear imported theme
applyTheme(value, isDarkMode)
}}>
<SelectTrigger className="w-full cursor-pointer">
<SelectValue placeholder="Tema seçin" />
</SelectTrigger>
<SelectContent className="max-h-60">
<div className="p-2">
{colorThemes.map((theme) => (
<SelectItem key={theme.value} value={theme.value} className="cursor-pointer">
<div className="flex items-center gap-2">
<div className="flex gap-1">
<div
className="w-3 h-3 rounded-full border border-border/20"
style={{ backgroundColor: theme.preset.styles.light.primary }}
/>
<div
className="w-3 h-3 rounded-full border border-border/20"
style={{ backgroundColor: theme.preset.styles.light.secondary }}
/>
<div
className="w-3 h-3 rounded-full border border-border/20"
style={{ backgroundColor: theme.preset.styles.light.accent }}
/>
<div
className="w-3 h-3 rounded-full border border-border/20"
style={{ backgroundColor: theme.preset.styles.light.muted }}
/>
</div>
<span>{theme.name}</span>
</div>
</SelectItem>
))}
</div>
</SelectContent>
</Select>
</div>
<Separator />
{/* Genişletilmiş temalar */}
<div className="space-y-3">
<div className="flex items-center justify-between">
<Label className="text-sm font-medium">Genişletilmiş temalar</Label>
<Button variant="outline" size="sm" onClick={handleRandomTweakcn} className="cursor-pointer">
<Dices className="h-3.5 w-3.5 mr-1.5" />
Rastgele
</Button>
</div>
<Select value={selectedTweakcnTheme} onValueChange={(value) => {
setSelectedTweakcnTheme(value)
setSelectedTheme("") // Clear other selection
setBrandColorsValues({}) // Clear brand colors state
setImportedTheme(null) // Clear imported theme
const selectedPreset = tweakcnThemes.find(t => t.value === value)?.preset
if (selectedPreset) {
applyTweakcnTheme(selectedPreset, isDarkMode)
}
}}>
<SelectTrigger className="w-full cursor-pointer">
<SelectValue placeholder="Tema seçin" />
</SelectTrigger>
<SelectContent className="max-h-60">
<div className="p-2">
{tweakcnThemes.map((theme) => (
<SelectItem key={theme.value} value={theme.value} className="cursor-pointer">
<div className="flex items-center gap-2">
<div className="flex gap-1">
<div
className="w-3 h-3 rounded-full border border-border/20"
style={{ backgroundColor: theme.preset.styles.light.primary }}
/>
<div
className="w-3 h-3 rounded-full border border-border/20"
style={{ backgroundColor: theme.preset.styles.light.secondary }}
/>
<div
className="w-3 h-3 rounded-full border border-border/20"
style={{ backgroundColor: theme.preset.styles.light.accent }}
/>
<div
className="w-3 h-3 rounded-full border border-border/20"
style={{ backgroundColor: theme.preset.styles.light.muted }}
/>
</div>
<span>{theme.name}</span>
</div>
</SelectItem>
))}
</div>
</SelectContent>
</Select>
</div>
<Separator />
{/* Köşe yuvarlama */}
<div className="space-y-3">
<Label className="text-sm font-medium">Köşe yuvarlama</Label>
<div className="grid grid-cols-5 gap-2">
{radiusOptions.map((option) => (
<div
key={option.value}
className={`relative cursor-pointer rounded-md p-3 border transition-colors ${
selectedRadius === option.value
? "border-primary"
: "border-border hover:border-border/60"
}`}
onClick={() => handleRadiusSelect(option.value)}
>
<div className="text-center">
<div className="text-xs font-medium">{option.name}</div>
</div>
</div>
))}
</div>
</div>
<Separator />
{/* Görünüm modu */}
<div className="space-y-3">
<Label className="text-sm font-medium">Görünüm modu</Label>
<div className="grid grid-cols-2 gap-2">
<Button
variant={!isDarkMode ? "secondary" : "outline"}
size="sm"
onClick={handleLightMode}
className="cursor-pointer"
>
<Sun className="h-4 w-4 mr-1" />
Açık
</Button>
<Button
variant={isDarkMode ? "secondary" : "outline"}
size="sm"
onClick={handleDarkMode}
className="cursor-pointer"
>
<Moon className="h-4 w-4 mr-1" />
Koyu
</Button>
</div>
</div>
<Separator />
{/* Tema içe aktar */}
<div className="space-y-3">
<Button
variant="outline"
size="lg"
onClick={onImportClick}
className="w-full cursor-pointer"
>
<Upload className="h-3.5 w-3.5 mr-1.5" />
Tema içe aktar
</Button>
<p className="text-muted-foreground text-xs">
tweakcn.com gibi araçlardan dışa aktardığınız JSON tema dosyasını yükleyebilirsiniz.
</p>
</div>
{/* Marka renkleri */}
<Accordion type="single" collapsible className="w-full border-b rounded-lg">
<AccordionItem value="brand-colors" className="border border-border rounded-lg overflow-hidden">
<AccordionTrigger className="px-4 py-3 hover:no-underline hover:bg-muted/50 transition-colors">
<Label className="text-sm font-medium cursor-pointer">Marka renkleri</Label>
</AccordionTrigger>
<AccordionContent className="px-4 pb-4 pt-2 space-y-3 border-t border-border bg-muted/20">
{baseColors.map((color) => (
<div key={color.cssVar} className="flex items-center justify-between">
<ColorPicker
label={color.name}
cssVar={color.cssVar}
value={brandColorsValues[color.cssVar] || ""}
onChange={handleColorChange}
/>
</div>
))}
</AccordionContent>
</AccordionItem>
</Accordion>
</div>
)
}