Başlıklar ve Seed
This commit is contained in:
parent
ce886ee5e1
commit
880329d3ae
37 changed files with 842 additions and 535 deletions
|
|
@ -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",
|
||||||
|
|
|
||||||
|
|
@ -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\//]
|
||||||
}));
|
}));
|
||||||
|
|
||||||
}));
|
}));
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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 = {
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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>
|
|
||||||
</>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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')}
|
||||||
|
|
|
||||||
|
|
@ -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">
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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 = {
|
||||||
|
|
@ -56,7 +57,7 @@ const Profile = () => {
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
|
|
||||||
const PROFILE_BASE_PATH = '/admin/profile'
|
const PROFILE_BASE_PATH = '/admin/profile'
|
||||||
|
|
||||||
const onTabChange = (val: string) => {
|
const onTabChange = (val: string) => {
|
||||||
if (settingsMenu[val]) {
|
if (settingsMenu[val]) {
|
||||||
setCurrentTab(settingsMenu[val].path)
|
setCurrentTab(settingsMenu[val].path)
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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">
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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 ? (
|
||||||
<>
|
<>
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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">
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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">
|
||||||
|
|
|
||||||
|
|
@ -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">
|
||||||
|
|
|
||||||
|
|
@ -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 */}
|
||||||
|
|
|
||||||
|
|
@ -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>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
|
>
|
||||||
← {translate('::Public.blog.backToBlog')}
|
← {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>
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue