Başlıklar ve Seed

This commit is contained in:
Sedat ÖZTÜRK 2025-08-14 10:10:56 +03:00
parent ce886ee5e1
commit 880329d3ae
37 changed files with 842 additions and 535 deletions

View file

@ -505,6 +505,12 @@
"en": "Abp Settings", "en": "Abp Settings",
"tr": "Abp Ayarları" "tr": "Abp Ayarları"
}, },
{
"resourceName": "Platform",
"key": "App.ChangeLog",
"en": "Change Log",
"tr": "Versiyon Günlüğü"
},
{ {
"resourceName": "Platform", "resourceName": "Platform",
"key": "App.BlogManagement", "key": "App.BlogManagement",
@ -6343,6 +6349,24 @@
"tr": "Ürünler", "tr": "Ürünler",
"en": "Products" "en": "Products"
}, },
{
"resourceName": "Platform",
"key": "Public.nav.checkout",
"tr": "Organizasyon Seçimi",
"en": "Select Organization"
},
{
"resourceName": "Platform",
"key": "Public.nav.payment",
"tr": "Ödeme",
"en": "Payment"
},
{
"resourceName": "Platform",
"key": "Public.nav.success",
"tr": "Sipariş Durumu",
"en": "Success"
},
{ {
"resourceName": "Platform", "resourceName": "Platform",
"key": "Public.nav.basket", "key": "Public.nav.basket",

View file

@ -82,11 +82,12 @@ define(['./workbox-54d0af47'], (function (workbox) { 'use strict';
"revision": "3ca0b8505b4bec776b69afdba2768812" "revision": "3ca0b8505b4bec776b69afdba2768812"
}, { }, {
"url": "index.html", "url": "index.html",
"revision": "0.172lu27b4eg" "revision": "0.6nc9e67jtoo"
}], {}); }], {});
workbox.cleanupOutdatedCaches(); workbox.cleanupOutdatedCaches();
workbox.registerRoute(new workbox.NavigationRoute(workbox.createHandlerBoundToURL("index.html"), { workbox.registerRoute(new workbox.NavigationRoute(workbox.createHandlerBoundToURL("index.html"), {
allowlist: [/^\/$/] allowlist: [/^\/$/],
denylist: [/^\/api\//]
})); }));
})); }));

View file

@ -5,8 +5,8 @@
<link rel="icon" type="image/svg+xml" href="/favicon.ico" /> <link rel="icon" type="image/svg+xml" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="theme-color" content="#000000" /> <meta name="theme-color" content="#000000" />
<meta name="description" content="Kurs Platform" /> <meta name="description" content="Sözsoft" />
<title>Kurs Platform</title> <title>Sözsoft</title>
</head> </head>
<body> <body>
<div id="root"></div> <div id="root"></div>

View file

@ -90,7 +90,7 @@ const Simple = ({ children, content, ...rest }: SimpleProps) => {
return ( return (
<div className="h-full"> <div className="h-full">
<Container className="flex flex-col flex-auto items-center justify-center min-w-0 h-full"> <Container className="flex flex-col flex-auto items-center justify-center min-w-0 h-full">
<Card className="min-w-[320px] md:min-w-[450px]" bodyClass="md:p-10"> <Card className="min-w-[320px] md:min-w-[450px]" bodyClass="md:p-6">
<div className="flex justify-between items-center mb-4"> <div className="flex justify-between items-center mb-4">
{!hasSubdomain() && ( {!hasSubdomain() && (
<a <a

View file

@ -2,9 +2,9 @@ import Logo from '@/components/template/Logo'
import { useStoreState } from '@/store' import { useStoreState } from '@/store'
const HeaderLogo = () => { const HeaderLogo = () => {
const mode = useStoreState((state) => state.theme.mode) const mode = useStoreState((state) => state.theme.mode)
return <Logo mode={mode} className="hidden md:block" /> return <Logo mode={mode} className="hidden md:block" imgClass="w-9/12" />
} }
export default HeaderLogo export default HeaderLogo

View file

@ -73,7 +73,10 @@ export const useMenuData = () => {
try { try {
// Flatten the hierarchy for API // Flatten the hierarchy for API
const flatten = (items: MenuItem[], parentCode: string | null = null): MenuItem[] => { const flatten = (
items: MenuItem[],
parentCode: string | undefined = undefined,
): MenuItem[] => {
const result: MenuItem[] = [] const result: MenuItem[] = []
items.forEach((item, index) => { items.forEach((item, index) => {
const flatItem = { const flatItem = {

View file

@ -3,6 +3,7 @@ import DoubleSidedImage from '@/components/shared/DoubleSidedImage'
import { Button } from '@/components/ui' import { Button } from '@/components/ui'
import { ROUTES_ENUM } from '@/routes/route.constant' import { ROUTES_ENUM } from '@/routes/route.constant'
import { useLocalization } from '@/utils/hooks/useLocalization' import { useLocalization } from '@/utils/hooks/useLocalization'
import { Helmet } from 'react-helmet'
import { MdArrowBack } from 'react-icons/md' import { MdArrowBack } from 'react-icons/md'
import { useNavigate } from 'react-router-dom' import { useNavigate } from 'react-router-dom'
@ -15,6 +16,11 @@ const AccessDenied = () => {
return ( return (
<Container className="h-full"> <Container className="h-full">
<Helmet
titleTemplate="%s | Kurs Platform"
title={translate('::' + 'Access Denied')}
defaultTitle="Kurs Platform"
></Helmet>
<div className="h-full flex flex-col items-center justify-center p-28"> <div className="h-full flex flex-col items-center justify-center p-28">
<DoubleSidedImage <DoubleSidedImage
src="/img/others/img-2.png" src="/img/others/img-2.png"
@ -29,7 +35,9 @@ const AccessDenied = () => {
size="xs" size="xs"
className="mt-2" className="mt-2"
variant="default" variant="default"
onClick={() => navigate(isAdminPath ? ROUTES_ENUM.protected.dashboard : ROUTES_ENUM.public.home)} onClick={() =>
navigate(isAdminPath ? ROUTES_ENUM.protected.dashboard : ROUTES_ENUM.public.home)
}
> >
<MdArrowBack /> <MdArrowBack />
</Button> </Button>

View file

@ -5,13 +5,11 @@ const Dashboard = () => {
const { translate } = useLocalization() const { translate } = useLocalization()
return ( return (
<> <Helmet
<Helmet titleTemplate="%s | Kurs Platform"
titleTemplate="%s | Kurs Platform" title={translate('::' + 'Dashboard')}
title={translate('::' + 'Home')} defaultTitle="Kurs Platform"
defaultTitle="Kurs Platform" ></Helmet>
></Helmet>
</>
) )
} }

View file

@ -1,5 +1,6 @@
import { ROUTES_ENUM } from '@/routes/route.constant' import { ROUTES_ENUM } from '@/routes/route.constant'
import { useLocalization } from '@/utils/hooks/useLocalization' import { useLocalization } from '@/utils/hooks/useLocalization'
import { Helmet } from 'react-helmet'
import { useNavigate } from 'react-router-dom' import { useNavigate } from 'react-router-dom'
const NotFoundPage = () => { const NotFoundPage = () => {
@ -11,6 +12,11 @@ const NotFoundPage = () => {
return ( return (
<div className="p-28"> <div className="p-28">
<Helmet
titleTemplate="%s | Kurs Platform"
title={translate('::' + 'Not Found')}
defaultTitle="Kurs Platform"
></Helmet>
<div className="flex items-center justify-center font-inter"> <div className="flex items-center justify-center font-inter">
<div className="text-[8rem] sm:text-[10rem] md:text-[12rem] font-bold bg-gradient-to-br from-primary to-secondary bg-clip-text animate-pulse"> <div className="text-[8rem] sm:text-[10rem] md:text-[12rem] font-bold bg-gradient-to-br from-primary to-secondary bg-clip-text animate-pulse">
404 404
@ -21,7 +27,9 @@ const NotFoundPage = () => {
</p> </p>
<div className="flex items-center justify-center font-inter"> <div className="flex items-center justify-center font-inter">
<button <button
onClick={() => navigate(isAdminPath ? ROUTES_ENUM.protected.dashboard : ROUTES_ENUM.public.home)} onClick={() =>
navigate(isAdminPath ? ROUTES_ENUM.protected.dashboard : ROUTES_ENUM.public.home)
}
className="px-6 py-3 bg-primary rounded-xl shadow hover:bg-secondary transition" className="px-6 py-3 bg-primary rounded-xl shadow hover:bg-secondary transition"
> >
{translate('::Public.notFound.button')} {translate('::Public.notFound.button')}

View file

@ -14,6 +14,7 @@ import merge from 'lodash/merge'
import { useEffect, useState } from 'react' import { useEffect, useState } from 'react'
import Log from './components/Log' import Log from './components/Log'
import LogFilter from './components/LogFilter' import LogFilter from './components/LogFilter'
import { Helmet } from 'react-helmet'
const itemsPerPage = 10 const itemsPerPage = 10
@ -80,6 +81,12 @@ const ActivityLog = () => {
return ( return (
<Container> <Container>
<Helmet
titleTemplate="%s | Kurs Platform"
title={translate('::' + 'Abp.Identity.ActivityLogs')}
defaultTitle="Kurs Platform"
></Helmet>
<AdaptableCard> <AdaptableCard>
<div className="grid lg:grid-cols-5 gap-8"> <div className="grid lg:grid-cols-5 gap-8">
<div className="col-span-4"> <div className="col-span-4">

View file

@ -263,6 +263,12 @@ function ChartEdit() {
return chartValues && roleList && userList && permissionOptions ? ( return chartValues && roleList && userList && permissionOptions ? (
<div> <div>
<Helmet
titleTemplate="%s | Kurs Platform"
title={chartCode}
defaultTitle="Kurs Platform"
></Helmet>
<Formik <Formik
initialValues={{ ...chartValues }} initialValues={{ ...chartValues }}
validationSchema={chartValidationSchema} validationSchema={chartValidationSchema}
@ -284,12 +290,6 @@ function ChartEdit() {
{({ touched, errors, resetForm, isSubmitting, values }) => ( {({ touched, errors, resetForm, isSubmitting, values }) => (
<Form> <Form>
<FormContainer size="sm"> <FormContainer size="sm">
<Helmet
titleTemplate="%s | Kurs Platform"
title={chartCode}
defaultTitle="Kurs Platform"
></Helmet>
<div className="lg:flex items-center justify-between mb-4 gap-3"> <div className="lg:flex items-center justify-between mb-4 gap-3">
<div className="mb-4 lg:mb-0"> <div className="mb-4 lg:mb-0">
<h4> <h4>

View file

@ -132,7 +132,7 @@ const Wizard = () => {
<Container> <Container>
<Helmet <Helmet
titleTemplate="%s | Kurs Platform" titleTemplate="%s | Kurs Platform"
title={translate('::' + 'Wizard')} title={translate('::' + 'App.Listforms.Wizard')}
defaultTitle="Kurs Platform" defaultTitle="Kurs Platform"
></Helmet> ></Helmet>

View file

@ -3,6 +3,7 @@ import Container from '@/components/shared/Container'
import Tabs from '@/components/ui/Tabs' import Tabs from '@/components/ui/Tabs'
import { useLocalization } from '@/utils/hooks/useLocalization' import { useLocalization } from '@/utils/hooks/useLocalization'
import { Suspense, lazy, useState } from 'react' import { Suspense, lazy, useState } from 'react'
import { Helmet } from 'react-helmet'
import { useLocation, useNavigate } from 'react-router-dom' import { useLocation, useNavigate } from 'react-router-dom'
type AccountSetting = { type AccountSetting = {
@ -66,6 +67,11 @@ const Profile = () => {
return ( return (
<Container> <Container>
<Helmet
titleTemplate="%s | Kurs Platform"
title={translate('::' + 'Abp.Identity.Profile')}
defaultTitle="Kurs Platform"
></Helmet>
<AdaptableCard> <AdaptableCard>
<Tabs value={currentTab} onChange={(val) => onTabChange(val)}> <Tabs value={currentTab} onChange={(val) => onTabChange(val)}>
<TabList> <TabList>

View file

@ -7,6 +7,7 @@ import { useLocalization } from '@/utils/hooks/useLocalization'
import { getAi } from '@/services/ai.service' import { getAi } from '@/services/ai.service'
import { AiDto } from '@/proxy/ai/models' import { AiDto } from '@/proxy/ai/models'
import { Container } from '@/components/shared' import { Container } from '@/components/shared'
import { Helmet } from 'react-helmet'
// Types // Types
type ChatType = 'chat' | 'query' | 'analyze' type ChatType = 'chat' | 'query' | 'analyze'
@ -264,6 +265,12 @@ const Assistant = () => {
// Render // Render
return ( return (
<Container> <Container>
<Helmet
titleTemplate="%s | Kurs Platform"
title={translate('::' + 'Abp.Identity.Ai')}
defaultTitle="Kurs Platform"
></Helmet>
<LoadAiPostsFromLocalStorage /> <LoadAiPostsFromLocalStorage />
<div className="h-[calc(100vh-140px)] flex flex-col"> <div className="h-[calc(100vh-140px)] flex flex-col">
<div className="flex-1 overflow-y-auto p-4 space-y-4"> <div className="flex-1 overflow-y-auto p-4 space-y-4">

View file

@ -11,6 +11,7 @@ import { useLocalization } from '@/utils/hooks/useLocalization'
import useTimeOutMessage from '@/utils/hooks/useTimeOutMessage' import useTimeOutMessage from '@/utils/hooks/useTimeOutMessage'
import { Field, Form, Formik } from 'formik' import { Field, Form, Formik } from 'formik'
import { useState } from 'react' import { useState } from 'react'
import { Helmet } from 'react-helmet'
import * as Yup from 'yup' import * as Yup from 'yup'
type ExtendLoginFormSchema = { type ExtendLoginFormSchema = {
@ -53,15 +54,27 @@ const ExtendLogin = () => {
return emailSent ? ( return emailSent ? (
<> <>
<h3 className="mb-1">{translate('::Abp.Account.ExtendLogin.Title')}</h3> <Helmet
<p>{translate('::Abp.Account.ExtendLogin.Description')}</p> titleTemplate="%s | Kurs Platform"
<div className="mt-4 text-center"> title={translate('AbpAccount::' + 'Abp.Account.ExtendLogin')}
<span>{translate('::Abp.Account.Backto')} </span> defaultTitle="Kurs Platform"
<ActionLink to={signInUrl}>{translate('::Abp.Account.SignIn')}</ActionLink> ></Helmet>
</div>{' '} <div>
<h3 className="mb-1">{translate('::Abp.Account.ExtendLogin.Title')}</h3>
<p>{translate('::Abp.Account.ExtendLogin.Description')}</p>
<div className="mt-4 text-center">
<span>{translate('::Abp.Account.Backto')} </span>
<ActionLink to={signInUrl}>{translate('::Abp.Account.SignIn')}</ActionLink>
</div>{' '}
</div>
</> </>
) : ( ) : (
<div> <div>
<Helmet
titleTemplate="%s | Kurs Platform"
title={translate('::' + 'Abp.Account.ExtendLogin')}
defaultTitle="Kurs Platform"
></Helmet>
<div className="mb-6"> <div className="mb-6">
<h3 className="mb-1">{translate('::Abp.Account.ExtendLogin')}</h3> <h3 className="mb-1">{translate('::Abp.Account.ExtendLogin')}</h3>
</div> </div>

View file

@ -13,6 +13,7 @@ import type { AxiosError } from 'axios'
import { Field, Form, Formik } from 'formik' import { Field, Form, Formik } from 'formik'
import { motion } from 'framer-motion' import { motion } from 'framer-motion'
import { useState } from 'react' import { useState } from 'react'
import { Helmet } from 'react-helmet'
import * as Yup from 'yup' import * as Yup from 'yup'
type ForgotPasswordFormSchema = { type ForgotPasswordFormSchema = {
@ -56,74 +57,81 @@ const ForgotPassword = () => {
} }
return ( return (
<motion.div <>
initial={{ opacity: 0, x: 100 }} <Helmet
animate={{ opacity: 1, x: 0 }} titleTemplate="%s | Kurs Platform"
transition={{ duration: 0.5, origin: 1 }} title={translate('::' + 'Abp.Account.ForgotPassword')}
> defaultTitle="Kurs Platform"
<div className="mb-6"> ></Helmet>
{emailSent ? ( <motion.div
<> initial={{ opacity: 0, x: 100 }}
<h3 className="mb-1">{translate('::Abp.Account.ForgotPassword.Checkyouremail')}</h3> animate={{ opacity: 1, x: 0 }}
<p>{translate('::Abp.Account.ForgotPassword.Checkyouremail.Message')}</p> transition={{ duration: 0.5, origin: 1 }}
</>
) : (
<>
<h3 className="mb-1">{translate('::Abp.Account.ForgotPassword')}</h3>
<p>{translate('::Abp.Account.ForgotPassword.Message')}</p>
</>
)}
</div>
{message && (
<Alert showIcon className="mb-4" type="danger">
{message}
</Alert>
)}
<Formik
initialValues={{
email: userName,
captchaResponse: '',
}}
validationSchema={validationSchema}
onSubmit={(values, { setSubmitting }) => {
if (!disableSubmit) {
onSendMail(values, setSubmitting)
} else {
setSubmitting(false)
}
}}
> >
{({ touched, errors, isSubmitting, setFieldValue }) => ( <div className="mb-6">
<Form> {emailSent ? (
<FormContainer> <>
<div className={emailSent ? 'hidden' : ''}> <h3 className="mb-1">{translate('::Abp.Account.ForgotPassword.Checkyouremail')}</h3>
<FormItem invalid={errors.email && touched.email} errorMessage={errors.email}> <p>{translate('::Abp.Account.ForgotPassword.Checkyouremail.Message')}</p>
<Field </>
type="email" ) : (
autoComplete="off" <>
name="email" <h3 className="mb-1">{translate('::Abp.Account.ForgotPassword')}</h3>
placeholder={translate('::Abp.Account.EmailAddress')} <p>{translate('::Abp.Account.ForgotPassword.Message')}</p>
component={Input} </>
/> )}
</FormItem> </div>
</div> {message && (
<Captcha <Alert showIcon className="mb-4" type="danger">
onError={() => setFieldValue('captchaResponse', '')} {message}
onExpire={() => setFieldValue('captchaResponse', '')} </Alert>
onSuccess={(token: string) => setFieldValue('captchaResponse', token)}
/>
<Button block loading={isSubmitting} variant="solid" type="submit">
{emailSent ? 'Resend Email' : 'Send Email'}
</Button>
<div className="mt-4 text-center">
<span>{translate('::Abp.Account.Backto')} </span>
<ActionLink to={signInUrl}>{translate('::Abp.Account.SignIn')}</ActionLink>
</div>
</FormContainer>
</Form>
)} )}
</Formik> <Formik
</motion.div> initialValues={{
email: userName,
captchaResponse: '',
}}
validationSchema={validationSchema}
onSubmit={(values, { setSubmitting }) => {
if (!disableSubmit) {
onSendMail(values, setSubmitting)
} else {
setSubmitting(false)
}
}}
>
{({ touched, errors, isSubmitting, setFieldValue }) => (
<Form>
<FormContainer>
<div className={emailSent ? 'hidden' : ''}>
<FormItem invalid={errors.email && touched.email} errorMessage={errors.email}>
<Field
type="email"
autoComplete="off"
name="email"
placeholder={translate('::Abp.Account.EmailAddress')}
component={Input}
/>
</FormItem>
</div>
<Captcha
onError={() => setFieldValue('captchaResponse', '')}
onExpire={() => setFieldValue('captchaResponse', '')}
onSuccess={(token: string) => setFieldValue('captchaResponse', token)}
/>
<Button block loading={isSubmitting} variant="solid" type="submit">
{emailSent ? 'Resend Email' : 'Send Email'}
</Button>
<div className="mt-4 text-center">
<span>{translate('::Abp.Account.Backto')} </span>
<ActionLink to={signInUrl}>{translate('::Abp.Account.SignIn')}</ActionLink>
</div>
</FormContainer>
</Form>
)}
</Formik>
</motion.div>
</>
) )
} }

View file

@ -21,6 +21,7 @@ import { useEffect, useRef, useState } from 'react'
import { useNavigate } from 'react-router-dom' import { useNavigate } from 'react-router-dom'
import * as Yup from 'yup' import * as Yup from 'yup'
import { getSubdomain } from '@/utils/subdomain' import { getSubdomain } from '@/utils/subdomain'
import { Helmet } from 'react-helmet'
type SignInFormSchema = { type SignInFormSchema = {
userName: string userName: string
@ -195,134 +196,141 @@ const Login = () => {
: undefined : undefined
return ( return (
<motion.div <>
initial={{ opacity: 0, x: 100 }} <Helmet
animate={{ opacity: 1, x: 0 }} titleTemplate="%s | Kurs Platform"
transition={{ duration: 0.5, origin: 1 }} title={translate('AbpAccount::' + 'Login')}
> defaultTitle="Kurs Platform"
<div className="mb-8 relative text-center"> ></Helmet>
<h3 className="mb-1 m-5">{translate('::Abp.Account.WelcomeBack')}</h3> <motion.div
<p>{translate('::Abp.Account.WelcomeBack.Message')}</p> initial={{ opacity: 0, x: 100 }}
</div> animate={{ opacity: 1, x: 0 }}
transition={{ duration: 0.5, origin: 1 }}
>
<div className="mb-8 relative text-center">
<h3 className="mb-1 m-5">{translate('::Abp.Account.WelcomeBack')}</h3>
<p>{translate('::Abp.Account.WelcomeBack.Message')}</p>
</div>
{isMultiTenant && ( {isMultiTenant && (
<> <>
<label className="form-label mb-2" style={tenantStyle}> <label className="form-label mb-2" style={tenantStyle}>
{translate('::Organization')} {translate('::Organization')}
</label> </label>
<div className="mb-4"> <div className="mb-4">
<Input <Input
placeholder={translate('::Organization')} placeholder={translate('::Organization')}
value={tenantName} value={tenantName}
onChange={(e) => setTenantName(e.target.value)} onChange={(e) => setTenantName(e.target.value)}
style={tenantStyle} style={tenantStyle}
aria-hidden={subDomainName && subDomainName !== defaultSubDomain ? 'true' : 'false'} aria-hidden={subDomainName && subDomainName !== defaultSubDomain ? 'true' : 'false'}
/> />
</div> </div>
</> </>
)} )}
<div> <div>
<Formik <Formik
initialValues={{ initialValues={{
userName: '', userName: '',
password: '', password: '',
twoFactorCode: '', twoFactorCode: '',
rememberMe: true, rememberMe: true,
captchaResponse: '', captchaResponse: '',
}} }}
validationSchema={validationSchema} validationSchema={validationSchema}
onSubmit={onSignIn} onSubmit={onSignIn}
> >
{({ touched, errors, isSubmitting, setFieldValue }) => ( {({ touched, errors, isSubmitting, setFieldValue }) => (
<Form> <Form>
<FormContainer> <FormContainer>
{!twoFactor && ( {!twoFactor && (
<FormItem <FormItem
label={translate('::Abp.Account.EmailAddress')} label={translate('::Abp.Account.EmailAddress')}
invalid={errors.userName && touched.userName} invalid={errors.userName && touched.userName}
errorMessage={errors.userName} errorMessage={errors.userName}
> >
<Field <Field
type="text" type="text"
autoComplete="off" autoComplete="off"
name="userName" name="userName"
placeholder={translate('::Abp.Account.EmailAddress')} placeholder={translate('::Abp.Account.EmailAddress')}
component={Input} component={Input}
/>
</FormItem>
)}
{!twoFactor && (
<FormItem
label={translate('::Abp.Account.Password')}
invalid={errors.password && touched.password}
errorMessage={errors.password}
>
<Field
autoComplete="off"
name="password"
placeholder={translate('::Abp.Account.Password')}
component={PasswordInput}
/>
</FormItem>
)}
{twoFactor && (
<FormItem
label={translate('::Abp.Account.2FACode')}
invalid={errors.twoFactorCode && touched.twoFactorCode}
errorMessage={errors.twoFactorCode}
>
<Field
autoComplete="off"
name="twoFactorCode"
placeholder={translate('::Abp.Account.2FACode')}
component={Input}
/>
</FormItem>
)}
<div className="flex justify-between mb-6">
<Field className="mb-0" name="rememberMe" component={Checkbox}>
{translate('::Abp.Account.RememberMe')}
</Field>
<ActionLink to={ROUTES_ENUM.authenticated.forgotPassword}>
{translate('::Abp.Account.ForgotPassword')}
</ActionLink>
</div>
{showCaptcha && (
<Captcha
ref={captchaRef}
onError={() => setFieldValue('captchaResponse', '')}
onExpire={() => setFieldValue('captchaResponse', '')}
onSuccess={(token: string) => setFieldValue('captchaResponse', token)}
/> />
</FormItem> )}
)}
{!twoFactor && (
<FormItem
label={translate('::Abp.Account.Password')}
invalid={errors.password && touched.password}
errorMessage={errors.password}
>
<Field
autoComplete="off"
name="password"
placeholder={translate('::Abp.Account.Password')}
component={PasswordInput}
/>
</FormItem>
)}
{twoFactor && (
<FormItem
label={translate('::Abp.Account.2FACode')}
invalid={errors.twoFactorCode && touched.twoFactorCode}
errorMessage={errors.twoFactorCode}
>
<Field
autoComplete="off"
name="twoFactorCode"
placeholder={translate('::Abp.Account.2FACode')}
component={Input}
/>
</FormItem>
)}
<div className="flex justify-between mb-6"> {message && (
<Field className="mb-0" name="rememberMe" component={Checkbox}> <Alert showIcon className="mb-4" type="success">
{translate('::Abp.Account.RememberMe')} {message}
</Field> </Alert>
<ActionLink to={ROUTES_ENUM.authenticated.forgotPassword}> )}
{translate('::Abp.Account.ForgotPassword')} {error && (
</ActionLink> <Alert showIcon className="mb-4" type="danger">
</div> {error}
{showCaptcha && ( </Alert>
<Captcha )}
ref={captchaRef}
onError={() => setFieldValue('captchaResponse', '')}
onExpire={() => setFieldValue('captchaResponse', '')}
onSuccess={(token: string) => setFieldValue('captchaResponse', token)}
/>
)}
{message && ( <Button block loading={isSubmitting} variant="solid" type="submit">
<Alert showIcon className="mb-4" type="success"> {isSubmitting ? 'Signing in...' : 'Sign In'}
{message} </Button>
</Alert> <div className="mt-4 text-center">
)} <span>{translate('::Abp.Account.SignUp.Message')} </span>
{error && ( <ActionLink to={ROUTES_ENUM.authenticated.register}>
<Alert showIcon className="mb-4" type="danger"> {translate('::Abp.Account.Register')}
{error} </ActionLink>
</Alert> </div>
)} </FormContainer>
</Form>
<Button block loading={isSubmitting} variant="solid" type="submit"> )}
{isSubmitting ? 'Signing in...' : 'Sign In'} </Formik>
</Button> </div>
<div className="mt-4 text-center"> </motion.div>
<span>{translate('::Abp.Account.SignUp.Message')} </span> </>
<ActionLink to={ROUTES_ENUM.authenticated.register}>
{translate('::Abp.Account.Register')}
</ActionLink>
</div>
</FormContainer>
</Form>
)}
</Formik>
</div>
</motion.div>
) )
} }

View file

@ -12,6 +12,7 @@ import { Field, Form, Formik } from 'formik'
import { useState } from 'react' import { useState } from 'react'
import * as Yup from 'yup' import * as Yup from 'yup'
import { useLocalization } from '@/utils/hooks/useLocalization' import { useLocalization } from '@/utils/hooks/useLocalization'
import { Helmet } from 'react-helmet'
type SignUpFormSchema = { type SignUpFormSchema = {
password: string password: string
@ -60,7 +61,13 @@ const Register = () => {
return ( return (
<> <>
<div className="mb-8"> <Helmet
titleTemplate="%s | Kurs Platform"
title={translate('AbpAccount::' + 'Register')}
defaultTitle="Kurs Platform"
></Helmet>
<div className="mb-4">
<h3 className="mb-1">{translate('::Abp.Account.Register.Title')}</h3> <h3 className="mb-1">{translate('::Abp.Account.Register.Title')}</h3>
<p>{translate('::Abp.Account.Register.Message')}</p> <p>{translate('::Abp.Account.Register.Message')}</p>
</div> </div>

View file

@ -10,6 +10,7 @@ import useTimeOutMessage from '@/utils/hooks/useTimeOutMessage'
import type { AxiosError } from 'axios' import type { AxiosError } from 'axios'
import { Field, Form, Formik } from 'formik' import { Field, Form, Formik } from 'formik'
import { useState } from 'react' import { useState } from 'react'
import { Helmet } from 'react-helmet'
import { useNavigate, useSearchParams } from 'react-router-dom' import { useNavigate, useSearchParams } from 'react-router-dom'
import * as Yup from 'yup' import * as Yup from 'yup'
@ -77,6 +78,11 @@ const ResetPassword = () => {
return ( return (
<div> <div>
<Helmet
titleTemplate="%s | Kurs Platform"
title={translate('AbpAccount::' + 'ResetPassword')}
defaultTitle="Kurs Platform"
></Helmet>
<div className="mb-6"> <div className="mb-6">
{resetComplete ? ( {resetComplete ? (
<> <>

View file

@ -7,6 +7,7 @@ import { ROUTES_ENUM } from '@/routes/route.constant'
import { store } from '@/store' import { store } from '@/store'
import Captcha from '@/components/shared/Captcha' import Captcha from '@/components/shared/Captcha'
import { useLocalization } from '@/utils/hooks/useLocalization' import { useLocalization } from '@/utils/hooks/useLocalization'
import { Helmet } from 'react-helmet'
type FormSchema = { type FormSchema = {
email: string email: string
@ -34,6 +35,12 @@ const SendConfirmationCode = () => {
return ( return (
<> <>
<Helmet
titleTemplate="%s | Kurs Platform"
title={translate('::' + 'Abp.Account.SendConfirmationCode')}
defaultTitle="Kurs Platform"
></Helmet>
<div className="mb-8"> <div className="mb-8">
<h3 className="mb-1">{translate('::Abp.Account.SendConfirmationCode')}</h3> <h3 className="mb-1">{translate('::Abp.Account.SendConfirmationCode')}</h3>
<p>{translate('::Abp.Account.SendConfirmationCode.Message')}</p> <p>{translate('::Abp.Account.SendConfirmationCode.Message')}</p>
@ -85,7 +92,9 @@ const SendConfirmationCode = () => {
</Button> </Button>
<div className="mt-4 text-center"> <div className="mt-4 text-center">
<span>{translate('::Abp.Account.Backto')} </span> <span>{translate('::Abp.Account.Backto')} </span>
<ActionLink to={ROUTES_ENUM.authenticated.login}>{translate('::Abp.Account.SignIn')}</ActionLink> <ActionLink to={ROUTES_ENUM.authenticated.login}>
{translate('::Abp.Account.SignIn')}
</ActionLink>
</div> </div>
</FormContainer> </FormContainer>
</Form> </Form>

View file

@ -3,6 +3,7 @@ import { Alert, Button } from '@/components/ui'
import { useNavigate, useParams } from 'react-router-dom' import { useNavigate, useParams } from 'react-router-dom'
import useAccount from '@/utils/hooks/useAccount' import useAccount from '@/utils/hooks/useAccount'
import { useLocalization } from '@/utils/hooks/useLocalization' import { useLocalization } from '@/utils/hooks/useLocalization'
import { Helmet } from 'react-helmet'
const VerifyConfirmationCode = () => { const VerifyConfirmationCode = () => {
const { userId, token } = useParams() const { userId, token } = useParams()
@ -19,6 +20,12 @@ const VerifyConfirmationCode = () => {
return ( return (
<> <>
<Helmet
titleTemplate="%s | Kurs Platform"
title={translate('::' + 'Abp.Account.VerifyConfirmationCode')}
defaultTitle="Kurs Platform"
></Helmet>
<div> <div>
{message && ( {message && (
<Alert showIcon className="mb-4" type="success"> <Alert showIcon className="mb-4" type="success">

View file

@ -2,14 +2,25 @@ import React from 'react'
import { EntityProvider } from '@/contexts/EntityContext' import { EntityProvider } from '@/contexts/EntityContext'
import DeveloperLayout from '@/components/layouts/DeveloperLayout' import DeveloperLayout from '@/components/layouts/DeveloperLayout'
import Dashboard from '@/components/developerKit/Dashboard' import Dashboard from '@/components/developerKit/Dashboard'
import { useLocalization } from '@/utils/hooks/useLocalization'
import { Helmet } from 'react-helmet'
const DashboardPage: React.FC = () => { const DashboardPage: React.FC = () => {
const { translate } = useLocalization()
return ( return (
<DeveloperLayout> <>
<EntityProvider> <Helmet
<Dashboard /> titleTemplate="%s | Kurs Platform"
</EntityProvider> title={translate('::' + 'App.DeveloperKit')}
</DeveloperLayout> defaultTitle="Kurs Platform"
></Helmet>
<DeveloperLayout>
<EntityProvider>
<Dashboard />
</EntityProvider>
</DeveloperLayout>
</>
) )
} }

View file

@ -1,91 +1,81 @@
import AdaptableCard from '@/components/shared/AdaptableCard' import AdaptableCard from '@/components/shared/AdaptableCard'
import Container from '@/components/shared/Container' import Container from '@/components/shared/Container'
import { useLocalization } from '@/utils/hooks/useLocalization'
import type { ReactNode } from 'react' import type { ReactNode } from 'react'
import { Helmet } from 'react-helmet'
type Log = { type Log = {
version: string version: string
date: string date: string
updateContent: string[] updateContent: string[]
} }
type LogProps = Omit<Log, 'updateContent'> & { type LogProps = Omit<Log, 'updateContent'> & {
border?: boolean border?: boolean
children?: ReactNode children?: ReactNode
} }
const logData: Log[] = [ const logData: Log[] = [
{ {
version: '1.0.3', version: '1.0.3',
date: '04 Şubat 2025', date: '04 Şubat 2025',
updateContent: [ updateContent: ['[Fix] GridBoxEditorComponent bug', '[Fix] TagBoxEditorComponent bug'],
'[Fix] GridBoxEditorComponent bug', },
'[Fix] TagBoxEditorComponent bug', {
], version: '1.0.2',
}, date: '27 Ocak 2025',
{ updateContent: ['[Add] jspdf kurulumu', '[Add] exceljs kurulumu', '[Add] file-saver kurulumu'],
version: '1.0.2', },
date: '27 Ocak 2025', {
updateContent: [ version: '1.0.1',
'[Add] jspdf kurulumu', date: '23 Ocak 2025',
'[Add] exceljs kurulumu', updateContent: ['[Fix] Bağımlılık güvenlik açığı'],
'[Add] file-saver kurulumu', },
], {
}, version: '1.0.0',
{ date: '20 Ocak 2025',
version: '1.0.1', updateContent: ['[Update] İlk yeni sürüm'],
date: '23 Ocak 2025', },
updateContent: [
'[Fix] Bağımlılık güvenlik açığı',
],
},
{
version: '1.0.0',
date: '20 Ocak 2025',
updateContent: [
'[Update] İlk yeni sürüm',
],
},
] ]
const Log = (props: LogProps) => { const Log = (props: LogProps) => {
return ( return (
<div className={`py-4 ${props.border && 'border-bottom'}`}> <div className={`py-4 ${props.border && 'border-bottom'}`}>
<div className="flex items-center"> <div className="flex items-center">
<h5 className="font-weight-normal mb-0 mr-3"> <h5 className="font-weight-normal mb-0 mr-3">{props.version}</h5>
{props.version} <code>{props.date}</code>
</h5> </div>
<code>{props.date}</code> <div className="api-container p-0 border-0 mt-3">{props.children}</div>
</div> </div>
<div className="api-container p-0 border-0 mt-3"> )
{props.children}
</div>
</div>
)
} }
const Changelog = () => { const Changelog = () => {
return ( const { translate } = useLocalization()
<Container>
<AdaptableCard> return (
<h4>Platform Güncelleme Günlüğü</h4> <Container>
{logData.map((elm) => ( <Helmet
<Log titleTemplate="%s | Kurs Platform"
key={elm.version} title={translate('::' + 'App.ChangeLog')}
version={`v${elm.version}`} defaultTitle="Kurs Platform"
date={elm.date} ></Helmet>
> <AdaptableCard>
{elm.updateContent.length > 0 ? ( <h4>Platform Güncelleme Günlüğü</h4>
<ul> {logData.map((elm) => (
{elm.updateContent.map((item, i) => ( <Log key={elm.version} version={`v${elm.version}`} date={elm.date}>
<li key={i}>- {item}</li> {elm.updateContent.length > 0 ? (
))} <ul>
</ul> {elm.updateContent.map((item, i) => (
) : null} <li key={i}>- {item}</li>
</Log>
))} ))}
</AdaptableCard> </ul>
</Container> ) : null}
) </Log>
))}
</AdaptableCard>
</Container>
)
} }
export default Changelog export default Changelog

View file

@ -4,8 +4,12 @@ import React, { useState, useEffect } from 'react'
import { useForumData } from './useForumData' import { useForumData } from './useForumData'
import { ForumView } from './forum/ForumView' import { ForumView } from './forum/ForumView'
import { Container } from '@/components/shared' import { Container } from '@/components/shared'
import { Helmet } from 'react-helmet'
import { useLocalization } from '@/utils/hooks/useLocalization'
export function Forum() { export function Forum() {
const { translate } = useLocalization()
const { user, tenant } = useStoreState((state) => state.auth) const { user, tenant } = useStoreState((state) => state.auth)
const { const {
categories, categories,
@ -47,6 +51,12 @@ export function Forum() {
return ( return (
<Container> <Container>
<Helmet
titleTemplate="%s | Kurs Platform"
title={translate('::' + 'App.Forum')}
defaultTitle="Kurs Platform"
></Helmet>
{error && ( {error && (
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-4"> <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-4">
<div className="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded relative"> <div className="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded relative">

View file

@ -2,6 +2,8 @@ import { useEffect } from 'react'
import { useForumData } from './useForumData' import { useForumData } from './useForumData'
import { AdminView } from './admin/AdminView' import { AdminView } from './admin/AdminView'
import { Container } from '@/components/shared' import { Container } from '@/components/shared'
import { Helmet } from 'react-helmet'
import { useLocalization } from '@/utils/hooks/useLocalization'
export function Management() { export function Management() {
const { const {
@ -31,6 +33,7 @@ export function Management() {
unmarkPostAsAcceptedAnswer, unmarkPostAsAcceptedAnswer,
clearError, clearError,
} = useForumData() } = useForumData()
const { translate } = useLocalization()
useEffect(() => { useEffect(() => {
const handleKeyDown = (e: KeyboardEvent) => { const handleKeyDown = (e: KeyboardEvent) => {
@ -54,6 +57,12 @@ export function Management() {
return ( return (
<Container> <Container>
<Helmet
titleTemplate="%s | Kurs Platform"
title={translate('::' + 'App.ForumManagement')}
defaultTitle="Kurs Platform"
></Helmet>
{error && ( {error && (
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-4"> <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-4">
<div className="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded relative"> <div className="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded relative">

View file

@ -4,11 +4,15 @@ import { MenuItem } from '@/@types/menu'
import { useMenuData } from '@/utils/hooks/useMenuData' import { useMenuData } from '@/utils/hooks/useMenuData'
import { AlertCircle, Loader2, Menu, Save } from 'lucide-react' import { AlertCircle, Loader2, Menu, Save } from 'lucide-react'
import { Container } from '@/components/shared' import { Container } from '@/components/shared'
import { Helmet } from 'react-helmet'
import { useLocalization } from '@/utils/hooks/useLocalization'
export const MenuManager = () => { export const MenuManager = () => {
const { menuItems, setMenuItems, loading, error, refetch, saveMenuData } = useMenuData() const { menuItems, setMenuItems, loading, error, refetch, saveMenuData } = useMenuData()
const [isDesignMode, setIsDesignMode] = useState(true) const [isDesignMode, setIsDesignMode] = useState(true)
const [isSaving, setIsSaving] = useState(false) const [isSaving, setIsSaving] = useState(false)
const { translate } = useLocalization()
const [saveMessage, setSaveMessage] = useState<{ const [saveMessage, setSaveMessage] = useState<{
type: 'success' | 'error' type: 'success' | 'error'
text: string text: string
@ -78,6 +82,12 @@ export const MenuManager = () => {
return ( return (
<Container> <Container>
<Helmet
titleTemplate="%s | Kurs Platform"
title={translate('::' + 'App.Menus.Manager')}
defaultTitle="Kurs Platform"
></Helmet>
<div className="bg-white rounded px-4 sm:px-4 lg:px-6 py-6"> <div className="bg-white rounded px-4 sm:px-4 lg:px-6 py-6">
<div className="flex items-center justify-between mb-6 flex-wrap gap-4"> <div className="flex items-center justify-between mb-6 flex-wrap gap-4">
{/* Sol kısım: Başlık */} {/* Sol kısım: Başlık */}

View file

@ -2,6 +2,7 @@ import React from 'react'
import { Users, Award, Clock, Globe2 } from 'lucide-react' import { Users, Award, Clock, Globe2 } from 'lucide-react'
import { useLocalization } from '@/utils/hooks/useLocalization' import { useLocalization } from '@/utils/hooks/useLocalization'
import { useCountUp } from '@/utils/hooks/useCountUp' import { useCountUp } from '@/utils/hooks/useCountUp'
import { Helmet } from 'react-helmet'
const About: React.FC = () => { const About: React.FC = () => {
const { translate } = useLocalization() const { translate } = useLocalization()
@ -12,97 +13,105 @@ const About: React.FC = () => {
const countriesCount = useCountUp({ end: 3, duration: 1500 }) const countriesCount = useCountUp({ end: 3, duration: 1500 })
return ( return (
<div className="min-h-screen bg-gray-50"> <>
{/* Hero Section */} <Helmet
<div className="relative bg-blue-900 text-white py-12"> titleTemplate="%s | Sözsoft"
<div title={translate('::' + 'Public.about.title')}
className="absolute inset-0 opacity-20" defaultTitle="Sözsoft"
style={{ ></Helmet>
backgroundImage:
'url("https://images.pexels.com/photos/3183183/pexels-photo-3183183.jpeg?auto=compress&cs=tinysrgb&w=1920")',
backgroundSize: 'cover',
backgroundPosition: 'center',
}}
></div>
<div className="container mx-auto pt-20 relative">
<h1 className="text-5xl font-bold ml-4 mt-3 mb-2 text-white">
{translate('::Public.about.title')}
</h1>
<p className="text-xl max-w-3xl ml-4">{translate('::Public.about.subtitle')}</p>
</div>
</div>
{/* Stats Section */} <div className="min-h-screen bg-gray-50">
<div className="py-10 bg-white"> {/* Hero Section */}
<div className="container mx-auto px-4"> <div className="relative bg-blue-900 text-white py-12">
<div className="grid grid-cols-1 md:grid-cols-4 gap-8"> <div
<div className="text-center" ref={clientsCount.elementRef}> className="absolute inset-0 opacity-20"
<Users className="w-12 h-12 text-blue-600 mx-auto mb-4" /> style={{
<div className="text-4xl font-bold text-gray-900 mb-2"> backgroundImage:
{clientsCount.displayValue} 'url("https://images.pexels.com/photos/3183183/pexels-photo-3183183.jpeg?auto=compress&cs=tinysrgb&w=1920")',
backgroundSize: 'cover',
backgroundPosition: 'center',
}}
></div>
<div className="container mx-auto pt-20 relative">
<h1 className="text-5xl font-bold ml-4 mt-3 mb-2 text-white">
{translate('::Public.about.title')}
</h1>
<p className="text-xl max-w-3xl ml-4">{translate('::Public.about.subtitle')}</p>
</div>
</div>
{/* Stats Section */}
<div className="py-10 bg-white">
<div className="container mx-auto px-4">
<div className="grid grid-cols-1 md:grid-cols-4 gap-8">
<div className="text-center" ref={clientsCount.elementRef}>
<Users className="w-12 h-12 text-blue-600 mx-auto mb-4" />
<div className="text-4xl font-bold text-gray-900 mb-2">
{clientsCount.displayValue}
</div>
<div className="text-gray-600">{translate('::Public.about.stats.clients')}</div>
</div> </div>
<div className="text-gray-600">{translate('::Public.about.stats.clients')}</div> <div className="text-center" ref={experienceCount.elementRef}>
</div> <Award className="w-12 h-12 text-blue-600 mx-auto mb-4" />
<div className="text-center" ref={experienceCount.elementRef}> <div className="text-4xl font-bold text-gray-900 mb-2">
<Award className="w-12 h-12 text-blue-600 mx-auto mb-4" /> {experienceCount.displayValue}
<div className="text-4xl font-bold text-gray-900 mb-2"> </div>
{experienceCount.displayValue} <div className="text-gray-600">{translate('::Public.about.stats.experience')}</div>
</div> </div>
<div className="text-gray-600">{translate('::Public.about.stats.experience')}</div> <div className="text-center">
</div> <Clock className="w-12 h-12 text-blue-600 mx-auto mb-4" />
<div className="text-center"> <div className="text-4xl font-bold text-gray-900 mb-2">7/24</div>
<Clock className="w-12 h-12 text-blue-600 mx-auto mb-4" /> <div className="text-gray-600">{translate('::Public.about.stats.support')}</div>
<div className="text-4xl font-bold text-gray-900 mb-2">7/24</div> </div>
<div className="text-gray-600">{translate('::Public.about.stats.support')}</div> <div className="text-center" ref={countriesCount.elementRef}>
</div> <Globe2 className="w-12 h-12 text-blue-600 mx-auto mb-4" />
<div className="text-center" ref={countriesCount.elementRef}> <div className="text-4xl font-bold text-gray-900 mb-2">
<Globe2 className="w-12 h-12 text-blue-600 mx-auto mb-4" /> {countriesCount.displayValue}
<div className="text-4xl font-bold text-gray-900 mb-2"> </div>
{countriesCount.displayValue} <div className="text-gray-600">{translate('::Public.about.stats.countries')}</div>
</div>
</div>
</div>
</div>
{/* Main Content */}
<div className="py-6">
<div className="container mx-auto px-4">
<div className="mb-6">
<div className="space-y-6 mx-auto mx-auto text-gray-800 text-lg leading-relaxed">
<p className="bg-white p-5 shadow-md border-l-4 border-blue-600">
{translate('::Public.about.description.part1')}
</p>
<p className="italic text-center text-blue-700 font-semibold">
{translate('::Public.about.description.motto')}
</p>
<p className="bg-white p-5 shadow-md border-l-4 border-blue-600">
{translate('::Public.about.description.part2')}
</p>
<p className="text-center text-blue-700 font-medium">
{translate('::Public.about.description.closing')}
</p>
</div>
</div>
<div className="grid grid-cols-1 lg:grid-cols-2 gap-12">
<div className="bg-white p-8 rounded-xl shadow-lg">
<h3 className="text-2xl font-bold text-gray-900 mb-4">
{translate('::Public.about.mission')}
</h3>
<p className="text-gray-700">{translate('::Public.about.mission.desc')}</p>
</div>
<div className="bg-white p-8 rounded-xl shadow-lg">
<h3 className="text-2xl font-bold text-gray-900 mb-4">
{translate('::Public.about.vision')}
</h3>
<p className="text-gray-700">{translate('::Public.about.vision.desc')}</p>
</div> </div>
<div className="text-gray-600">{translate('::Public.about.stats.countries')}</div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</>
{/* Main Content */}
<div className="py-6">
<div className="container mx-auto px-4">
<div className="mb-6">
<div className="space-y-6 mx-auto mx-auto text-gray-800 text-lg leading-relaxed">
<p className="bg-white p-5 shadow-md border-l-4 border-blue-600">
{translate('::Public.about.description.part1')}
</p>
<p className="italic text-center text-blue-700 font-semibold">
{translate('::Public.about.description.motto')}
</p>
<p className="bg-white p-5 shadow-md border-l-4 border-blue-600">
{translate('::Public.about.description.part2')}
</p>
<p className="text-center text-blue-700 font-medium">
{translate('::Public.about.description.closing')}
</p>
</div>
</div>
<div className="grid grid-cols-1 lg:grid-cols-2 gap-12">
<div className="bg-white p-8 rounded-xl shadow-lg">
<h3 className="text-2xl font-bold text-gray-900 mb-4">
{translate('::Public.about.mission')}
</h3>
<p className="text-gray-700">{translate('::Public.about.mission.desc')}</p>
</div>
<div className="bg-white p-8 rounded-xl shadow-lg">
<h3 className="text-2xl font-bold text-gray-900 mb-4">
{translate('::Public.about.vision')}
</h3>
<p className="text-gray-700">{translate('::Public.about.vision.desc')}</p>
</div>
</div>
</div>
</div>
</div>
) )
} }

View file

@ -1,30 +1,31 @@
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from 'react'
import { Link } from "react-router-dom"; import { Link } from 'react-router-dom'
import { Calendar, Clock, User, Tag, Search } from "lucide-react"; import { Calendar, Clock, User, Tag, Search } from 'lucide-react'
import { format } from "date-fns"; import { format } from 'date-fns'
import { tr } from "date-fns/locale"; import { tr } from 'date-fns/locale'
import { BlogCategory, BlogPost } from "@/proxy/blog/blog"; import { BlogCategory, BlogPost } from '@/proxy/blog/blog'
import { blogService } from "@/services/blog.service"; import { blogService } from '@/services/blog.service'
import { useLocalization } from "@/utils/hooks/useLocalization"; import { useLocalization } from '@/utils/hooks/useLocalization'
import { Helmet } from 'react-helmet'
const Blog = () => { const Blog = () => {
const { translate } = useLocalization() const { translate } = useLocalization()
const [posts, setPosts] = useState<BlogPost[]>([]); const [posts, setPosts] = useState<BlogPost[]>([])
const [categories, setCategories] = useState<BlogCategory[]>([]); const [categories, setCategories] = useState<BlogCategory[]>([])
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true)
const [selectedCategory, setSelectedCategory] = useState<string>(""); const [selectedCategory, setSelectedCategory] = useState<string>('')
const [searchQuery, setSearchQuery] = useState(""); const [searchQuery, setSearchQuery] = useState('')
const [currentPage, setCurrentPage] = useState(1); const [currentPage, setCurrentPage] = useState(1)
const [totalPages, setTotalPages] = useState(1); const [totalPages, setTotalPages] = useState(1)
useEffect(() => { useEffect(() => {
loadBlogData(); loadBlogData()
}, [currentPage, selectedCategory]); }, [currentPage, selectedCategory])
const loadBlogData = async () => { const loadBlogData = async () => {
try { try {
setLoading(true); setLoading(true)
const [postsData, categoriesData] = await Promise.all([ const [postsData, categoriesData] = await Promise.all([
blogService.getPosts({ blogService.getPosts({
page: currentPage, page: currentPage,
@ -33,29 +34,29 @@ const Blog = () => {
search: searchQuery, search: searchQuery,
}), }),
blogService.getCategories(), blogService.getCategories(),
]); ])
setPosts(postsData.items.filter(a=> a.isPublished)); setPosts(postsData.items.filter((a) => a.isPublished))
setTotalPages(postsData.totalPages); setTotalPages(postsData.totalPages)
setCategories(categoriesData.filter(a=> a.isActive)); setCategories(categoriesData.filter((a) => a.isActive))
} catch (error) { } catch (error) {
console.error("Blog verileri yüklenemedi:", error); console.error('Blog verileri yüklenemedi:', error)
setPosts([]); setPosts([])
} finally { } finally {
setLoading(false); setLoading(false)
} }
}; }
const handleSearch = (e: React.FormEvent) => { const handleSearch = (e: React.FormEvent) => {
e.preventDefault(); e.preventDefault()
setCurrentPage(1); setCurrentPage(1)
loadBlogData(); loadBlogData()
}; }
const handleCategoryChange = (categoryId: string) => { const handleCategoryChange = (categoryId: string) => {
setSelectedCategory(categoryId); setSelectedCategory(categoryId)
setCurrentPage(1); setCurrentPage(1)
}; }
if (loading && posts.length === 0) { if (loading && posts.length === 0) {
return ( return (
@ -64,11 +65,16 @@ const Blog = () => {
<p className="mt-4 text-gray-600">Blog yazıları yükleniyor...</p> <p className="mt-4 text-gray-600">Blog yazıları yükleniyor...</p>
</div> </div>
</div> </div>
); )
} }
return ( return (
<div className="min-h-screen bg-gray-50"> <div className="min-h-screen bg-gray-50">
<Helmet
titleTemplate="%s | Sözsoft"
title={translate('::' + 'Public.blog.title')}
defaultTitle="Sözsoft"
></Helmet>
{/* Hero Section */} {/* Hero Section */}
<div className="relative bg-blue-900 text-white py-12"> <div className="relative bg-blue-900 text-white py-12">
<div <div
@ -76,12 +82,14 @@ const Blog = () => {
style={{ style={{
backgroundImage: backgroundImage:
'url("https://images.pexels.com/photos/3183164/pexels-photo-3183164.jpeg?auto=compress&cs=tinysrgb&w=1920")', 'url("https://images.pexels.com/photos/3183164/pexels-photo-3183164.jpeg?auto=compress&cs=tinysrgb&w=1920")',
backgroundSize: "cover", backgroundSize: 'cover',
backgroundPosition: "center", backgroundPosition: 'center',
}} }}
></div> ></div>
<div className="container mx-auto pt-20 relative"> <div className="container mx-auto pt-20 relative">
<h1 className="text-5xl font-bold ml-4 mt-3 mb-2 text-white">{translate('::Public.blog.title')}</h1> <h1 className="text-5xl font-bold ml-4 mt-3 mb-2 text-white">
{translate('::Public.blog.title')}
</h1>
<p className="text-xl max-w-3xl ml-4">{translate('::Public.blog.subtitle')}</p> <p className="text-xl max-w-3xl ml-4">{translate('::Public.blog.subtitle')}</p>
</div> </div>
</div> </div>
@ -107,11 +115,11 @@ const Blog = () => {
{/* Category Filter */} {/* Category Filter */}
<div className="flex gap-2 flex-wrap"> <div className="flex gap-2 flex-wrap">
<button <button
onClick={() => handleCategoryChange("")} onClick={() => handleCategoryChange('')}
className={`px-4 py-2 rounded-lg transition-colors ${ className={`px-4 py-2 rounded-lg transition-colors ${
selectedCategory === "" selectedCategory === ''
? "bg-blue-600 text-white" ? 'bg-blue-600 text-white'
: "bg-gray-200 text-gray-700 hover:bg-gray-300" : 'bg-gray-200 text-gray-700 hover:bg-gray-300'
}`} }`}
> >
Tümü Tümü
@ -122,11 +130,11 @@ const Blog = () => {
onClick={() => handleCategoryChange(category.id)} onClick={() => handleCategoryChange(category.id)}
className={`px-4 py-2 rounded-lg transition-colors ${ className={`px-4 py-2 rounded-lg transition-colors ${
selectedCategory === category.id selectedCategory === category.id
? "bg-blue-600 text-white" ? 'bg-blue-600 text-white'
: "bg-gray-200 text-gray-700 hover:bg-gray-300" : 'bg-gray-200 text-gray-700 hover:bg-gray-300'
}`} }`}
> >
{ translate('::Public.' + category.name)} ({category.postCount}) {translate('::Public.' + category.name)} ({category.postCount})
</button> </button>
))} ))}
</div> </div>
@ -138,24 +146,16 @@ const Blog = () => {
<div className="container mx-auto px-4 py-16"> <div className="container mx-auto px-4 py-16">
{!Array.isArray(posts) || posts.length === 0 ? ( {!Array.isArray(posts) || posts.length === 0 ? (
<div className="text-center py-12"> <div className="text-center py-12">
<p className="text-gray-600 text-lg"> <p className="text-gray-600 text-lg">Henüz blog yazısı bulunmuyor.</p>
Henüz blog yazısı bulunmuyor.
</p>
</div> </div>
) : ( ) : (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8"> <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
{posts.map((post) => ( {posts.map((post) => (
<Link <Link to={`/blog/${post.slug || post.id}`} key={post.id} className="block">
to={`/blog/${post.slug || post.id}`}
key={post.id}
className="block"
>
<article className="bg-white rounded-xl shadow-lg overflow-hidden hover:shadow-xl transition-shadow h-full flex flex-col"> <article className="bg-white rounded-xl shadow-lg overflow-hidden hover:shadow-xl transition-shadow h-full flex flex-col">
<div className="aspect-w-16 aspect-h-9 relative"> <div className="aspect-w-16 aspect-h-9 relative">
<img <img
src={ src={post.coverImage}
post.coverImage
}
alt={post.title} alt={post.title}
className="object-cover w-full h-48" className="object-cover w-full h-48"
/> />
@ -167,7 +167,9 @@ const Blog = () => {
<h2 className="text-xl font-bold text-gray-900 mb-3 hover:text-blue-600 transition-colors"> <h2 className="text-xl font-bold text-gray-900 mb-3 hover:text-blue-600 transition-colors">
{translate('::Public.' + post.title)} {translate('::Public.' + post.title)}
</h2> </h2>
<p className="text-gray-600 mb-4 flex-1">{translate('::Public.' + post.summary)}</p> <p className="text-gray-600 mb-4 flex-1">
{translate('::Public.' + post.summary)}
</p>
{/* Tags */} {/* Tags */}
{post.tags.length > 0 && ( {post.tags.length > 0 && (
@ -191,11 +193,9 @@ const Blog = () => {
</div> </div>
<div className="flex items-center"> <div className="flex items-center">
<Calendar size={16} className="mr-1" /> <Calendar size={16} className="mr-1" />
{format( {format(new Date(post.publishedAt || post.creationTime), 'dd MMM yyyy', {
new Date(post.publishedAt || post.creationTime), locale: tr,
"dd MMM yyyy", })}
{ locale: tr }
)}
</div> </div>
</div> </div>
</div> </div>
@ -223,8 +223,8 @@ const Blog = () => {
onClick={() => setCurrentPage(i + 1)} onClick={() => setCurrentPage(i + 1)}
className={`px-4 py-2 rounded-lg ${ className={`px-4 py-2 rounded-lg ${
currentPage === i + 1 currentPage === i + 1
? "bg-blue-600 text-white" ? 'bg-blue-600 text-white'
: "border border-gray-300 hover:bg-gray-50" : 'border border-gray-300 hover:bg-gray-50'
}`} }`}
> >
{i + 1} {i + 1}
@ -232,9 +232,7 @@ const Blog = () => {
))} ))}
<button <button
onClick={() => onClick={() => setCurrentPage(Math.min(totalPages, currentPage + 1))}
setCurrentPage(Math.min(totalPages, currentPage + 1))
}
disabled={currentPage === totalPages} disabled={currentPage === totalPages}
className="px-4 py-2 border border-gray-300 rounded-lg hover:bg-gray-50 disabled:opacity-50 disabled:cursor-not-allowed" className="px-4 py-2 border border-gray-300 rounded-lg hover:bg-gray-50 disabled:opacity-50 disabled:cursor-not-allowed"
> >
@ -267,7 +265,7 @@ const Blog = () => {
</div> </div>
</div> </div>
</div> </div>
); )
}; }
export default Blog; export default Blog

View file

@ -7,6 +7,7 @@ import { blogService } from '@/services/blog.service'
import { useLocalization } from '@/utils/hooks/useLocalization' import { useLocalization } from '@/utils/hooks/useLocalization'
import { useStoreState } from '@/store/store' import { useStoreState } from '@/store/store'
import { ROUTES_ENUM } from '@/routes/route.constant' import { ROUTES_ENUM } from '@/routes/route.constant'
import { Helmet } from 'react-helmet'
interface PostData { interface PostData {
image?: string image?: string
@ -78,9 +79,17 @@ const BlogDetail: React.FC = () => {
return ( return (
<div className="min-h-screen bg-gray-50"> <div className="min-h-screen bg-gray-50">
<Helmet
titleTemplate="%s | Sözsoft"
title={translate('::' + 'Public.blog.title')}
defaultTitle="Sözsoft"
></Helmet>
<div className="relative bg-blue-900 text-white py-12"></div> <div className="relative bg-blue-900 text-white py-12"></div>
<div className="container mx-auto px-4"> <div className="container mx-auto px-4">
<Link to={ROUTES_ENUM.public.blog} className="text-blue-600 hover:underline mt-4 mb-4 inline-block"> <Link
to={ROUTES_ENUM.public.blog}
className="text-blue-600 hover:underline mt-4 mb-4 inline-block"
>
&larr; {translate('::Public.blog.backToBlog')} &larr; {translate('::Public.blog.backToBlog')}
</Link> </Link>
{postData.image && ( {postData.image && (
@ -90,7 +99,9 @@ const BlogDetail: React.FC = () => {
className="w-full h-96 object-cover rounded-lg mb-8" className="w-full h-96 object-cover rounded-lg mb-8"
/> />
)} )}
<h1 className="text-4xl font-bold text-gray-900 mb-6">{translate('::Public.' + blogPost.title)}</h1> <h1 className="text-4xl font-bold text-gray-900 mb-6">
{translate('::Public.' + blogPost.title)}
</h1>
<div className="flex items-center text-sm text-gray-500 space-x-4 mb-8"> <div className="flex items-center text-sm text-gray-500 space-x-4 mb-8">
<div className="flex items-center"> <div className="flex items-center">
<span>{postData.author?.name}</span> <span>{postData.author?.name}</span>
@ -105,7 +116,10 @@ const BlogDetail: React.FC = () => {
<div <div
className="prose max-w-none text-gray-800" className="prose max-w-none text-gray-800"
dangerouslySetInnerHTML={{ dangerouslySetInnerHTML={{
__html: currentLang == 'tr' ? translate(blogPost.contentTr!!) : translate(blogPost.contentEn!!), __html:
currentLang == 'tr'
? translate(blogPost.contentTr!!)
: translate(blogPost.contentEn!!),
}} }}
/> />
</div> </div>

View file

@ -8,12 +8,16 @@ import {
removeItemFromCart, removeItemFromCart,
updateCartItemQuantity, updateCartItemQuantity,
} from '@/utils/cartUtils' } from '@/utils/cartUtils'
import { useLocalization } from '@/utils/hooks/useLocalization'
import React, { useState, useEffect } from 'react' import React, { useState, useEffect } from 'react'
import { Helmet } from 'react-helmet'
import { useNavigate } from 'react-router-dom' import { useNavigate } from 'react-router-dom'
const Checkout: React.FC = () => { const Checkout: React.FC = () => {
const navigate = useNavigate() const navigate = useNavigate()
const [isCartOpen, setIsCartOpen] = useState(false) const [isCartOpen, setIsCartOpen] = useState(false)
const { translate } = useLocalization()
const [cartState, setCartState] = useState<CartState>(() => { const [cartState, setCartState] = useState<CartState>(() => {
const savedCart = localStorage.getItem('cartState') const savedCart = localStorage.getItem('cartState')
return savedCart ? JSON.parse(savedCart) : initialCartState return savedCart ? JSON.parse(savedCart) : initialCartState
@ -55,6 +59,12 @@ const Checkout: React.FC = () => {
return ( return (
<div className="min-h-screen bg-gray-50"> <div className="min-h-screen bg-gray-50">
<Helmet
titleTemplate="%s | Kurs Platform"
title={translate('::' + 'Public.nav.checkout')}
defaultTitle="Kurs Platform"
></Helmet>
{/* Hero Section */} {/* Hero Section */}
<div className="relative bg-blue-900 text-white py-12"> <div className="relative bg-blue-900 text-white py-12">
<div <div

View file

@ -1,4 +1,4 @@
import React from "react"; import React from 'react'
import { import {
Mail, Mail,
Phone, Phone,
@ -8,14 +8,21 @@ import {
CalendarDays, CalendarDays,
CalendarCheck, CalendarCheck,
MessageCircle, MessageCircle,
} from "lucide-react"; } from 'lucide-react'
import { useLocalization } from "@/utils/hooks/useLocalization"; import { useLocalization } from '@/utils/hooks/useLocalization'
import { Helmet } from 'react-helmet'
const Contact: React.FC = () => { const Contact: React.FC = () => {
const { translate } = useLocalization() const { translate } = useLocalization()
return ( return (
<div className="min-h-screen bg-gray-50"> <div className="min-h-screen bg-gray-50">
<Helmet
titleTemplate="%s | Sözsoft"
title={translate('::' + 'Public.contact.title')}
defaultTitle="Sözsoft"
></Helmet>
{/* Hero Section */} {/* Hero Section */}
<div className="relative bg-blue-900 text-white py-12"> <div className="relative bg-blue-900 text-white py-12">
<div <div
@ -23,12 +30,14 @@ const Contact: React.FC = () => {
style={{ style={{
backgroundImage: backgroundImage:
'url("https://images.pexels.com/photos/3183171/pexels-photo-3183171.jpeg?auto=compress&cs=tinysrgb&w=1920")', 'url("https://images.pexels.com/photos/3183171/pexels-photo-3183171.jpeg?auto=compress&cs=tinysrgb&w=1920")',
backgroundSize: "cover", backgroundSize: 'cover',
backgroundPosition: "center", backgroundPosition: 'center',
}} }}
></div> ></div>
<div className="container mx-auto pt-20 relative"> <div className="container mx-auto pt-20 relative">
<h1 className="text-5xl font-bold ml-4 mt-3 mb-2 text-white">{translate('::Public.contact.title')}</h1> <h1 className="text-5xl font-bold ml-4 mt-3 mb-2 text-white">
{translate('::Public.contact.title')}
</h1>
<p className="text-xl max-w-3xl ml-4">{translate('::Public.contact.subtitle')}</p> <p className="text-xl max-w-3xl ml-4">{translate('::Public.contact.subtitle')}</p>
</div> </div>
</div> </div>
@ -46,9 +55,7 @@ const Contact: React.FC = () => {
<div className="flex items-start space-x-4"> <div className="flex items-start space-x-4">
<MapPin className="w-6 h-6 text-blue-600 flex-shrink-0 mt-1" /> <MapPin className="w-6 h-6 text-blue-600 flex-shrink-0 mt-1" />
<div> <div>
<p className="text-gray-600"> <p className="text-gray-600">{translate('::Public.contact.address.full')}</p>
{translate('::Public.contact.address.full')}
</p>
</div> </div>
</div> </div>
<div className="flex items-start space-x-4"> <div className="flex items-start space-x-4">
@ -96,9 +103,7 @@ const Contact: React.FC = () => {
className="w-24 object-contain mt-1 flex-shrink-0" className="w-24 object-contain mt-1 flex-shrink-0"
/> />
<div> <div>
<h3 className="font-semibold text-gray-900"> <h3 className="font-semibold text-gray-900">Özlem Öztürk</h3>
Özlem Öztürk
</h3>
<p className="text-gray-600"> <p className="text-gray-600">
03663 / Enpara 03663 / Enpara
<br /> <br />
@ -163,7 +168,7 @@ const Contact: React.FC = () => {
</div> </div>
</div> </div>
</div> </div>
); )
}; }
export default Contact; export default Contact

View file

@ -17,46 +17,123 @@ import {
} from 'lucide-react' } from 'lucide-react'
import { useLocalization } from '@/utils/hooks/useLocalization' import { useLocalization } from '@/utils/hooks/useLocalization'
import { ROUTES_ENUM } from '@/routes/route.constant' import { ROUTES_ENUM } from '@/routes/route.constant'
import { Helmet } from 'react-helmet'
const Home: React.FC = () => { const Home: React.FC = () => {
const { translate } = useLocalization() const { translate } = useLocalization()
const features = [ const features = [
{ icon: <Users className="w-12 h-12 text-blue-500" />, title: translate('::Public.features.reliable'), description: translate('::Public.features.reliable.desc') }, {
{ icon: <Calendar className="w-12 h-12 text-blue-500" />, title: translate('::Public.features.rapid'), description: translate('::Public.features.rapid.desc') }, icon: <Users className="w-12 h-12 text-blue-500" />,
{ icon: <BookOpen className="w-12 h-12 text-blue-500" />, title: translate('::Public.features.expert'), description: translate('::Public.features.expert.desc') }, title: translate('::Public.features.reliable'),
{ icon: <CreditCard className="w-12 h-12 text-blue-500" />, title: translate('::Public.features.muhasebe'), description: translate('::Public.features.muhasebe.desc') }, description: translate('::Public.features.reliable.desc'),
{ icon: <MessageSquare className="w-12 h-12 text-blue-500" />, title: translate('::Public.features.iletisim'), description: translate('::Public.features.iletisim.desc') }, },
{ icon: <Phone className="w-12 h-12 text-blue-500" />, title: translate('::Public.features.mobil'), description: translate('::Public.features.mobil.desc') }, {
{ icon: <BarChart className="w-12 h-12 text-blue-500" />, title: translate('::Public.features.scalable'), description: translate('::Public.features.scalable.desc') }, icon: <Calendar className="w-12 h-12 text-blue-500" />,
{ icon: <Shield className="w-12 h-12 text-blue-500" />, title: translate('::Public.features.guvenlik'), description: translate('::Public.features.guvenlik.desc') }, title: translate('::Public.features.rapid'),
description: translate('::Public.features.rapid.desc'),
},
{
icon: <BookOpen className="w-12 h-12 text-blue-500" />,
title: translate('::Public.features.expert'),
description: translate('::Public.features.expert.desc'),
},
{
icon: <CreditCard className="w-12 h-12 text-blue-500" />,
title: translate('::Public.features.muhasebe'),
description: translate('::Public.features.muhasebe.desc'),
},
{
icon: <MessageSquare className="w-12 h-12 text-blue-500" />,
title: translate('::Public.features.iletisim'),
description: translate('::Public.features.iletisim.desc'),
},
{
icon: <Phone className="w-12 h-12 text-blue-500" />,
title: translate('::Public.features.mobil'),
description: translate('::Public.features.mobil.desc'),
},
{
icon: <BarChart className="w-12 h-12 text-blue-500" />,
title: translate('::Public.features.scalable'),
description: translate('::Public.features.scalable.desc'),
},
{
icon: <Shield className="w-12 h-12 text-blue-500" />,
title: translate('::Public.features.guvenlik'),
description: translate('::Public.features.guvenlik.desc'),
},
] ]
const solutions = [ const solutions = [
{ icon: <Monitor className="w-16 h-16 text-white" />, title: translate('::Public.solutions.web.title'), description: translate('::Public.solutions.web.desc'), color: 'bg-blue-600' }, {
{ icon: <Smartphone className="w-16 h-16 text-white" />, title: translate('::Public.solutions.mobile.title'), description: translate('::Public.solutions.mobile.desc'), color: 'bg-purple-600' }, icon: <Monitor className="w-16 h-16 text-white" />,
{ icon: <Server className="w-16 h-16 text-white" />, title: translate('::Public.solutions.custom.title'), description: translate('::Public.solutions.custom.desc'), color: 'bg-green-600' }, title: translate('::Public.solutions.web.title'),
{ icon: <Database className="w-16 h-16 text-white" />, title: translate('::Public.solutions.database.title'), description: translate('::Public.solutions.database.desc'), color: 'bg-red-600' }, description: translate('::Public.solutions.web.desc'),
color: 'bg-blue-600',
},
{
icon: <Smartphone className="w-16 h-16 text-white" />,
title: translate('::Public.solutions.mobile.title'),
description: translate('::Public.solutions.mobile.desc'),
color: 'bg-purple-600',
},
{
icon: <Server className="w-16 h-16 text-white" />,
title: translate('::Public.solutions.custom.title'),
description: translate('::Public.solutions.custom.desc'),
color: 'bg-green-600',
},
{
icon: <Database className="w-16 h-16 text-white" />,
title: translate('::Public.solutions.database.title'),
description: translate('::Public.solutions.database.desc'),
color: 'bg-red-600',
},
] ]
return ( return (
<div className="min-h-screen"> <div className="min-h-screen">
<Helmet
titleTemplate="%s | Sözsoft"
title={translate('::' + 'App.Home')}
defaultTitle="Sözsoft"
></Helmet>
{/* Hero */} {/* Hero */}
<div className="relative min-h-screen"> <div className="relative min-h-screen">
<div className="absolute inset-0 bg-gradient-to-br from-blue-900 via-indigo-900 to-purple-900"></div> <div className="absolute inset-0 bg-gradient-to-br from-blue-900 via-indigo-900 to-purple-900"></div>
<div className="absolute inset-0 opacity-20" style={{ backgroundImage: 'url("https://images.pexels.com/photos/3183150/pexels-photo-3183150.jpeg?auto=compress&cs=tinysrgb&w=1920")', backgroundSize: 'cover', backgroundPosition: 'center' }}></div> <div
className="absolute inset-0 opacity-20"
style={{
backgroundImage:
'url("https://images.pexels.com/photos/3183150/pexels-photo-3183150.jpeg?auto=compress&cs=tinysrgb&w=1920")',
backgroundSize: 'cover',
backgroundPosition: 'center',
}}
></div>
<div className="relative container mx-auto px-4 pt-32 pb-16"> <div className="relative container mx-auto px-4 pt-32 pb-16">
<div className="max-w-4xl mx-auto text-center"> <div className="max-w-4xl mx-auto text-center">
<h1 className="text-3xl md:text-5xl font-bold mb-6 text-white">{translate('::Public.hero.title')}</h1> <h1 className="text-3xl md:text-5xl font-bold mb-6 text-white">
<p className="text-xl md:text-2xl text-gray-300 mb-12">{translate('::Public.hero.subtitle')}</p> {translate('::Public.hero.title')}
</h1>
<p className="text-xl md:text-2xl text-gray-300 mb-12">
{translate('::Public.hero.subtitle')}
</p>
<div className="flex flex-col md:flex-row justify-center gap-6 mb-16"> <div className="flex flex-col md:flex-row justify-center gap-6 mb-16">
<Link to={ROUTES_ENUM.public.contact} className="inline-flex items-center justify-center px-8 py-4 bg-gradient-to-r from-blue-500 to-purple-500 hover:from-blue-600 hover:to-purple-600 text-white rounded-lg font-semibold transition-all transform hover:scale-105"> <Link
{translate('::Public.hero.cta.consultation')} <ArrowRight className="ml-2" size={20} /> to={ROUTES_ENUM.public.contact}
className="inline-flex items-center justify-center px-8 py-4 bg-gradient-to-r from-blue-500 to-purple-500 hover:from-blue-600 hover:to-purple-600 text-white rounded-lg font-semibold transition-all transform hover:scale-105"
>
{translate('::Public.hero.cta.consultation')}{' '}
<ArrowRight className="ml-2" size={20} />
</Link> </Link>
<Link to={ROUTES_ENUM.public.products} className="inline-flex items-center justify-center px-8 py-4 bg-white/10 hover:bg-white/20 text-white rounded-lg font-semibold backdrop-blur-sm transition-all transform hover:scale-105"> <Link
to={ROUTES_ENUM.public.products}
className="inline-flex items-center justify-center px-8 py-4 bg-white/10 hover:bg-white/20 text-white rounded-lg font-semibold backdrop-blur-sm transition-all transform hover:scale-105"
>
{translate('::Public.hero.cta.discover')} {translate('::Public.hero.cta.discover')}
</Link> </Link>
</div> </div>
@ -64,17 +141,23 @@ const Home: React.FC = () => {
<div className="grid grid-cols-1 md:grid-cols-3 gap-8 max-w-4xl mx-auto"> <div className="grid grid-cols-1 md:grid-cols-3 gap-8 max-w-4xl mx-auto">
<div className="bg-white/5 backdrop-blur-sm rounded-2xl p-8 text-center hover:scale-105 hover:bg-white/10 transition-all"> <div className="bg-white/5 backdrop-blur-sm rounded-2xl p-8 text-center hover:scale-105 hover:bg-white/10 transition-all">
<Calendar className="mx-auto mb-4 text-blue-400" size={40} /> <Calendar className="mx-auto mb-4 text-blue-400" size={40} />
<h3 className="text-xl font-semibold mb-3 text-white">{translate('::Public.hero.service1.title')}</h3> <h3 className="text-xl font-semibold mb-3 text-white">
{translate('::Public.hero.service1.title')}
</h3>
<p className="text-gray-300">{translate('::Public.hero.service1.desc')}</p> <p className="text-gray-300">{translate('::Public.hero.service1.desc')}</p>
</div> </div>
<div className="bg-white/5 backdrop-blur-sm rounded-2xl p-8 text-center hover:scale-105 hover:bg-white/10 transition-all"> <div className="bg-white/5 backdrop-blur-sm rounded-2xl p-8 text-center hover:scale-105 hover:bg-white/10 transition-all">
<Users className="mx-auto mb-4 text-purple-400" size={40} /> <Users className="mx-auto mb-4 text-purple-400" size={40} />
<h3 className="text-xl font-semibold mb-3 text-white">{translate('::Public.hero.service2.title')}</h3> <h3 className="text-xl font-semibold mb-3 text-white">
{translate('::Public.hero.service2.title')}
</h3>
<p className="text-gray-300">{translate('::Public.hero.service2.desc')}</p> <p className="text-gray-300">{translate('::Public.hero.service2.desc')}</p>
</div> </div>
<div className="bg-white/5 backdrop-blur-sm rounded-2xl p-8 text-center hover:scale-105 hover:bg-white/10 transition-all"> <div className="bg-white/5 backdrop-blur-sm rounded-2xl p-8 text-center hover:scale-105 hover:bg-white/10 transition-all">
<Shield className="mx-auto mb-4 text-indigo-400" size={40} /> <Shield className="mx-auto mb-4 text-indigo-400" size={40} />
<h3 className="text-xl font-semibold mb-3 text-white">{translate('::Public.hero.service3.title')}</h3> <h3 className="text-xl font-semibold mb-3 text-white">
{translate('::Public.hero.service3.title')}
</h3>
<p className="text-gray-300">{translate('::Public.hero.service3.desc')}</p> <p className="text-gray-300">{translate('::Public.hero.service3.desc')}</p>
</div> </div>
</div> </div>
@ -86,13 +169,20 @@ const Home: React.FC = () => {
<section className="py-20 bg-white"> <section className="py-20 bg-white">
<div className="container mx-auto px-4"> <div className="container mx-auto px-4">
<div className="text-center mb-16"> <div className="text-center mb-16">
<h2 className="text-4xl font-bold text-gray-900 mb-4">{translate('::Public.features.title')}</h2> <h2 className="text-4xl font-bold text-gray-900 mb-4">
<p className="text-xl text-gray-600 max-w-2xl mx-auto">{translate('::Public.features.subtitle')}</p> {translate('::Public.features.title')}
</h2>
<p className="text-xl text-gray-600 max-w-2xl mx-auto">
{translate('::Public.features.subtitle')}
</p>
</div> </div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-8"> <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-8">
{features.map((feature, i) => ( {features.map((feature, i) => (
<div key={i} className="p-8 bg-white rounded-xl shadow-lg hover:shadow-xl transition-shadow"> <div
key={i}
className="p-8 bg-white rounded-xl shadow-lg hover:shadow-xl transition-shadow"
>
<div className="mb-6">{feature.icon}</div> <div className="mb-6">{feature.icon}</div>
<h3 className="text-xl font-semibold text-gray-900 mb-4">{feature.title}</h3> <h3 className="text-xl font-semibold text-gray-900 mb-4">{feature.title}</h3>
<p className="text-gray-600">{feature.description}</p> <p className="text-gray-600">{feature.description}</p>
@ -106,13 +196,20 @@ const Home: React.FC = () => {
<section className="py-20 bg-gray-50"> <section className="py-20 bg-gray-50">
<div className="container mx-auto px-4"> <div className="container mx-auto px-4">
<div className="text-center mb-16"> <div className="text-center mb-16">
<h2 className="text-4xl font-bold text-gray-900 mb-4">{translate('::Public.solutions.title')}</h2> <h2 className="text-4xl font-bold text-gray-900 mb-4">
<p className="text-xl text-gray-600 max-w-2xl mx-auto">{translate('::Public.solutions.subtitle')}</p> {translate('::Public.solutions.title')}
</h2>
<p className="text-xl text-gray-600 max-w-2xl mx-auto">
{translate('::Public.solutions.subtitle')}
</p>
</div> </div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-8"> <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-8">
{solutions.map((s, i) => ( {solutions.map((s, i) => (
<div key={i} className="group relative overflow-hidden rounded-2xl transition-all hover:scale-105"> <div
key={i}
className="group relative overflow-hidden rounded-2xl transition-all hover:scale-105"
>
<div className={`${s.color} p-8 h-full`}> <div className={`${s.color} p-8 h-full`}>
<div className="mb-6">{s.icon}</div> <div className="mb-6">{s.icon}</div>
<h3 className="text-2xl font-semibold text-white mb-4">{s.title}</h3> <h3 className="text-2xl font-semibold text-white mb-4">{s.title}</h3>
@ -127,9 +224,14 @@ const Home: React.FC = () => {
{/* Call to Action */} {/* Call to Action */}
<section className="bg-blue-600 py-16"> <section className="bg-blue-600 py-16">
<div className="container mx-auto px-4 text-center"> <div className="container mx-auto px-4 text-center">
<h2 className="text-3xl font-bold text-white mb-4">{translate('::Public.common.getStarted')}</h2> <h2 className="text-3xl font-bold text-white mb-4">
{translate('::Public.common.getStarted')}
</h2>
<p className="text-white text-lg mb-8">{translate('::Public.common.contact')}</p> <p className="text-white text-lg mb-8">{translate('::Public.common.contact')}</p>
<Link to={ROUTES_ENUM.public.contact} className="bg-white text-blue-600 px-8 py-3 rounded-lg font-semibold hover:bg-blue-50 transition-colors"> <Link
to={ROUTES_ENUM.public.contact}
className="bg-white text-blue-600 px-8 py-3 rounded-lg font-semibold hover:bg-blue-50 transition-colors"
>
{translate('::Public.common.learnMore')} {translate('::Public.common.learnMore')}
</Link> </Link>
</div> </div>

View file

@ -10,7 +10,9 @@ import {
removeItemFromCart, removeItemFromCart,
updateCartItemQuantity, updateCartItemQuantity,
} from '@/utils/cartUtils' } from '@/utils/cartUtils'
import { useLocalization } from '@/utils/hooks/useLocalization'
import React, { useState, useEffect } from 'react' import React, { useState, useEffect } from 'react'
import { Helmet } from 'react-helmet'
import { useNavigate } from 'react-router-dom' import { useNavigate } from 'react-router-dom'
const Payment: React.FC = () => { const Payment: React.FC = () => {
@ -18,6 +20,7 @@ const Payment: React.FC = () => {
const [tenant, setTenant] = useState<CustomTenantDto | null>(null) const [tenant, setTenant] = useState<CustomTenantDto | null>(null)
const [isCartOpen, setIsCartOpen] = useState(false) const [isCartOpen, setIsCartOpen] = useState(false)
const [cartState, setCartState] = useState<CartState>(initialCartState) const [cartState, setCartState] = useState<CartState>(initialCartState)
const { translate } = useLocalization()
useEffect(() => { useEffect(() => {
const tenantData = localStorage.getItem('tenantData') // ✅ güncellendi const tenantData = localStorage.getItem('tenantData') // ✅ güncellendi
@ -109,6 +112,12 @@ const Payment: React.FC = () => {
return ( return (
<div className="min-h-screen bg-gray-50"> <div className="min-h-screen bg-gray-50">
<Helmet
titleTemplate="%s | Sözsoft"
title={translate('::' + 'Public.nav.payment')}
defaultTitle="Sözsoft"
></Helmet>
{/* Hero Section */} {/* Hero Section */}
<div className="relative bg-blue-900 text-white py-12"> <div className="relative bg-blue-900 text-white py-12">
<div <div

View file

@ -11,12 +11,16 @@ import {
setCartGlobalPeriod, setCartGlobalPeriod,
updateCartItemQuantity, updateCartItemQuantity,
} from '@/utils/cartUtils' } from '@/utils/cartUtils'
import { useLocalization } from '@/utils/hooks/useLocalization'
import React, { useState, useEffect } from 'react' import React, { useState, useEffect } from 'react'
import { Helmet } from 'react-helmet'
import { useNavigate } from 'react-router-dom' import { useNavigate } from 'react-router-dom'
const Products: React.FC = () => { const Products: React.FC = () => {
const [searchQuery, setSearchQuery] = useState('') const [searchQuery, setSearchQuery] = useState('')
const [isCartOpen, setIsCartOpen] = useState(false) const [isCartOpen, setIsCartOpen] = useState(false)
const { translate } = useLocalization()
const [cartState, setCartState] = useState<CartState>(() => { const [cartState, setCartState] = useState<CartState>(() => {
// Load cart state from localStorage if available // Load cart state from localStorage if available
const savedCart = localStorage.getItem('cartState') const savedCart = localStorage.getItem('cartState')
@ -70,6 +74,12 @@ const Products: React.FC = () => {
return ( return (
<div className="min-h-screen bg-gray-50"> <div className="min-h-screen bg-gray-50">
<Helmet
titleTemplate="%s | Sözsoft"
title={translate('::' + 'Public.nav.products')}
defaultTitle="Sözsoft"
></Helmet>
{/* Hero Section */} {/* Hero Section */}
<div className="relative bg-blue-900 text-white py-12"> <div className="relative bg-blue-900 text-white py-12">
<div <div

View file

@ -1,16 +1,9 @@
import React from "react"; import React from 'react'
import { import { Code2, Globe2, Server, Users, Shield, Settings, CheckCircle } from 'lucide-react'
Code2, import { Link } from 'react-router-dom'
Globe2, import { useLocalization } from '@/utils/hooks/useLocalization'
Server, import { ROUTES_ENUM } from '@/routes/route.constant'
Users, import { Helmet } from 'react-helmet'
Shield,
Settings,
CheckCircle,
} from "lucide-react";
import { Link } from "react-router-dom";
import { useLocalization } from "@/utils/hooks/useLocalization";
import { ROUTES_ENUM } from "@/routes/route.constant";
const Services: React.FC = () => { const Services: React.FC = () => {
const { translate } = useLocalization() const { translate } = useLocalization()
@ -88,7 +81,7 @@ const Services: React.FC = () => {
translate('::Public.services.consulting.features.training'), translate('::Public.services.consulting.features.training'),
], ],
}, },
]; ]
const supportPlans = [ const supportPlans = [
{ {
@ -127,10 +120,16 @@ const Services: React.FC = () => {
translate('::Public.services.support.sms.features.api'), translate('::Public.services.support.sms.features.api'),
], ],
}, },
]; ]
return ( return (
<div className="min-h-screen bg-gray-50"> <div className="min-h-screen bg-gray-50">
<Helmet
titleTemplate="%s | Sözsoft"
title={translate('::' + 'Public.nav.services')}
defaultTitle="Sözsoft"
></Helmet>
{/* Hero Section */} {/* Hero Section */}
<div className="relative bg-blue-900 text-white py-12"> <div className="relative bg-blue-900 text-white py-12">
<div <div
@ -138,12 +137,14 @@ const Services: React.FC = () => {
style={{ style={{
backgroundImage: backgroundImage:
'url("https://images.pexels.com/photos/3183173/pexels-photo-3183173.jpeg?auto=compress&cs=tinysrgb&w=1920")', 'url("https://images.pexels.com/photos/3183173/pexels-photo-3183173.jpeg?auto=compress&cs=tinysrgb&w=1920")',
backgroundSize: "cover", backgroundSize: 'cover',
backgroundPosition: "center", backgroundPosition: 'center',
}} }}
></div> ></div>
<div className="container mx-auto pt-20 relative"> <div className="container mx-auto pt-20 relative">
<h1 className="text-5xl font-bold ml-4 mt-3 mb-2 text-white">{translate('::Public.services.title')}</h1> <h1 className="text-5xl font-bold ml-4 mt-3 mb-2 text-white">
{translate('::Public.services.title')}
</h1>
<p className="text-xl max-w-3xl ml-4">{translate('::Public.services.subtitle')}</p> <p className="text-xl max-w-3xl ml-4">{translate('::Public.services.subtitle')}</p>
</div> </div>
</div> </div>
@ -158,16 +159,11 @@ const Services: React.FC = () => {
className="bg-white rounded-xl shadow-lg p-8 hover:shadow-xl transition-shadow" className="bg-white rounded-xl shadow-lg p-8 hover:shadow-xl transition-shadow"
> >
<div className="mb-6">{service.icon}</div> <div className="mb-6">{service.icon}</div>
<h3 className="text-2xl font-bold text-gray-900 mb-4"> <h3 className="text-2xl font-bold text-gray-900 mb-4">{service.title}</h3>
{service.title}
</h3>
<p className="text-gray-600 mb-6">{service.description}</p> <p className="text-gray-600 mb-6">{service.description}</p>
<ul className="space-y-2"> <ul className="space-y-2">
{service.features.map((feature, fIndex) => ( {service.features.map((feature, fIndex) => (
<li <li key={fIndex} className="flex items-center text-gray-700">
key={fIndex}
className="flex items-center text-gray-700"
>
<span className="w-2 h-2 bg-blue-600 rounded-full mr-2"></span> <span className="w-2 h-2 bg-blue-600 rounded-full mr-2"></span>
{feature} {feature}
</li> </li>
@ -187,18 +183,12 @@ const Services: React.FC = () => {
</h2> </h2>
<div className="grid grid-cols-1 md:grid-cols-3 gap-8"> <div className="grid grid-cols-1 md:grid-cols-3 gap-8">
{supportPlans.map((plan, index) => ( {supportPlans.map((plan, index) => (
<div <div key={index} className="bg-white rounded-xl shadow-lg p-8 border border-gray-200">
key={index}
className="bg-white rounded-xl shadow-lg p-8 border border-gray-200"
>
<h3 className="text-xl font-bold mb-4">{plan.title}</h3> <h3 className="text-xl font-bold mb-4">{plan.title}</h3>
<ul className="space-y-3 mb-8"> <ul className="space-y-3 mb-8">
{plan.features.map((feature, fIndex) => ( {plan.features.map((feature, fIndex) => (
<li <li key={fIndex} className="flex items-center space-x-2 text-gray-700">
key={fIndex}
className="flex items-center space-x-2 text-gray-700"
>
<CheckCircle className="w-5 h-5 text-green-500 flex-shrink-0" /> <CheckCircle className="w-5 h-5 text-green-500 flex-shrink-0" />
<span>{feature}</span> <span>{feature}</span>
</li> </li>
@ -219,7 +209,9 @@ const Services: React.FC = () => {
{/* Call to Action */} {/* Call to Action */}
<div className="bg-blue-900 text-white py-16"> <div className="bg-blue-900 text-white py-16">
<div className="container mx-auto px-4 text-center"> <div className="container mx-auto px-4 text-center">
<h2 className="text-3xl font-bold mb-6 text-white">{translate('::Public.services.cta.title')}</h2> <h2 className="text-3xl font-bold mb-6 text-white">
{translate('::Public.services.cta.title')}
</h2>
<p className="text-xl mb-8 max-w-2xl mx-auto"> <p className="text-xl mb-8 max-w-2xl mx-auto">
{translate('::Public.services.cta.description')} {translate('::Public.services.cta.description')}
</p> </p>
@ -232,7 +224,7 @@ const Services: React.FC = () => {
</div> </div>
</div> </div>
</div> </div>
); )
}; }
export default Services; export default Services

View file

@ -1,10 +1,13 @@
import { OrderSuccess } from '@/components/orders/OrderSuccess' import { OrderSuccess } from '@/components/orders/OrderSuccess'
import { useLocalization } from '@/utils/hooks/useLocalization'
import React, { useState, useEffect } from 'react' import React, { useState, useEffect } from 'react'
import { Helmet } from 'react-helmet'
import { useNavigate } from 'react-router-dom' import { useNavigate } from 'react-router-dom'
const Success: React.FC = () => { const Success: React.FC = () => {
const navigate = useNavigate() const navigate = useNavigate()
const [orderId, setOrderId] = useState<string>('') const [orderId, setOrderId] = useState<string>('')
const { translate } = useLocalization()
useEffect(() => { useEffect(() => {
// Get order ID from local storage // Get order ID from local storage
@ -28,6 +31,11 @@ const Success: React.FC = () => {
return ( return (
<div className="min-h-screen bg-gray-50"> <div className="min-h-screen bg-gray-50">
<Helmet
titleTemplate="%s | Sözsoft"
title={translate('::' + 'Public.nav.success')}
defaultTitle="Sözsoft"
></Helmet>
<div className="relative bg-blue-900 text-white py-12"> <div className="relative bg-blue-900 text-white py-12">
<div <div
className="absolute inset-0 opacity-20" className="absolute inset-0 opacity-20"

View file

@ -121,7 +121,7 @@ const Settings = () => {
<Container className="h-full"> <Container className="h-full">
<Helmet <Helmet
titleTemplate="%s | Kurs Platform" titleTemplate="%s | Kurs Platform"
title={translate('::' + 'Settings')} title={translate('::' + 'App.Settings')}
defaultTitle="Kurs Platform" defaultTitle="Kurs Platform"
></Helmet> ></Helmet>