Integrated query builder

This commit is contained in:
Umar Adilov 2025-12-17 13:41:43 +05:00
parent f96934d27d
commit f98e1fc715
17 changed files with 1135 additions and 213 deletions

View File

@ -25,6 +25,7 @@
"@radix-ui/react-toast": "^1.2.11", "@radix-ui/react-toast": "^1.2.11",
"@radix-ui/react-tooltip": "^1.2.6", "@radix-ui/react-tooltip": "^1.2.6",
"@reduxjs/toolkit": "^2.7.0", "@reduxjs/toolkit": "^2.7.0",
"@taylordb/query-builder": "^0.10.1",
"aos": "^2.3.4", "aos": "^2.3.4",
"axios": "^1.9.0", "axios": "^1.9.0",
"class-variance-authority": "^0.7.1", "class-variance-authority": "^0.7.1",

127
pnpm-lock.yaml generated
View File

@ -53,6 +53,9 @@ importers:
'@reduxjs/toolkit': '@reduxjs/toolkit':
specifier: ^2.7.0 specifier: ^2.7.0
version: 2.7.0(react-redux@9.2.0(@types/react@19.1.2)(react@19.1.0)(redux@5.0.1))(react@19.1.0) version: 2.7.0(react-redux@9.2.0(@types/react@19.1.2)(react@19.1.0)(redux@5.0.1))(react@19.1.0)
'@taylordb/query-builder':
specifier: ^0.10.1
version: 0.10.1
aos: aos:
specifier: ^2.3.4 specifier: ^2.3.4
version: 2.3.4 version: 2.3.4
@ -1089,6 +1092,9 @@ packages:
'@rushstack/eslint-patch@1.11.0': '@rushstack/eslint-patch@1.11.0':
resolution: {integrity: sha512-zxnHvoMQVqewTJr/W4pKjF0bMGiKJv1WX7bSrkl46Hg0QjESbzBROWK0Wg4RphzSOS5Jiy7eFimmM3UgMrMZbQ==} resolution: {integrity: sha512-zxnHvoMQVqewTJr/W4pKjF0bMGiKJv1WX7bSrkl46Hg0QjESbzBROWK0Wg4RphzSOS5Jiy7eFimmM3UgMrMZbQ==}
'@socket.io/component-emitter@3.1.2':
resolution: {integrity: sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==}
'@standard-schema/spec@1.0.0': '@standard-schema/spec@1.0.0':
resolution: {integrity: sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==} resolution: {integrity: sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==}
@ -1189,6 +1195,12 @@ packages:
'@tailwindcss/postcss@4.1.4': '@tailwindcss/postcss@4.1.4':
resolution: {integrity: sha512-bjV6sqycCEa+AQSt2Kr7wpGF1bOZJ5wsqnLEkqSbM/JEHxx/yhMH8wHmdkPyApF9xhHeMSwnnkDUUMMM/hYnXw==} resolution: {integrity: sha512-bjV6sqycCEa+AQSt2Kr7wpGF1bOZJ5wsqnLEkqSbM/JEHxx/yhMH8wHmdkPyApF9xhHeMSwnnkDUUMMM/hYnXw==}
'@taylordb/query-builder@0.10.1':
resolution: {integrity: sha512-WUyohbO8R+xFC+t+zfTP5VSLakGlBu2gsdWbFWwru4KqLNFW/NCsIvSDhzNObVbtbSgvkf+oVC0li+/z9uZYig==}
'@taylordb/shared@0.4.4':
resolution: {integrity: sha512-Xykr4I26JapNLePkapBGjz15t9Ep1iLs30VbfCcc2z30x8Qy/2tm+sSa5LcacCP2EaxNVDp2UuYKhZ7kOWBLBQ==}
'@trivago/prettier-plugin-sort-imports@5.2.2': '@trivago/prettier-plugin-sort-imports@5.2.2':
resolution: {integrity: sha512-fYDQA9e6yTNmA13TLVSA+WMQRc5Bn/c0EUBditUHNfMMxN7M82c38b1kEggVE3pLpZ0FwkwJkUEKMiOi52JXFA==} resolution: {integrity: sha512-fYDQA9e6yTNmA13TLVSA+WMQRc5Bn/c0EUBditUHNfMMxN7M82c38b1kEggVE3pLpZ0FwkwJkUEKMiOi52JXFA==}
engines: {node: '>18.12'} engines: {node: '>18.12'}
@ -1589,6 +1601,15 @@ packages:
supports-color: supports-color:
optional: true optional: true
debug@4.3.7:
resolution: {integrity: sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==}
engines: {node: '>=6.0'}
peerDependencies:
supports-color: '*'
peerDependenciesMeta:
supports-color:
optional: true
debug@4.4.0: debug@4.4.0:
resolution: {integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==} resolution: {integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==}
engines: {node: '>=6.0'} engines: {node: '>=6.0'}
@ -1653,6 +1674,13 @@ packages:
emoji-regex@9.2.2: emoji-regex@9.2.2:
resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==}
engine.io-client@6.6.3:
resolution: {integrity: sha512-T0iLjnyNWahNyv/lcjS2y4oE358tVS/SYQNxYXGAJ9/GLgH4VCvOQ/mhTjqU88mLZCQgiG8RIegFHYCdVC+j5w==}
engine.io-parser@5.2.3:
resolution: {integrity: sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==}
engines: {node: '>=10.0.0'}
enhanced-resolve@5.18.1: enhanced-resolve@5.18.1:
resolution: {integrity: sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==} resolution: {integrity: sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==}
engines: {node: '>=10.13.0'} engines: {node: '>=10.13.0'}
@ -1836,6 +1864,9 @@ packages:
resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==}
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
eventemitter3@5.0.1:
resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==}
fast-deep-equal@3.1.3: fast-deep-equal@3.1.3:
resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
@ -1847,6 +1878,9 @@ packages:
resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==}
engines: {node: '>=8.6.0'} engines: {node: '>=8.6.0'}
fast-json-patch@3.1.1:
resolution: {integrity: sha512-vf6IHUX2SBcA+5/+4883dsIjpBTqmfBjmYiWK1savxQmFk4JfBMLa7ynTYOs1Rolp/T1betJxHiGD3g1Mn8lUQ==}
fast-json-stable-stringify@2.1.0: fast-json-stable-stringify@2.1.0:
resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==}
@ -2685,6 +2719,14 @@ packages:
simple-swizzle@0.2.2: simple-swizzle@0.2.2:
resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==} resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==}
socket.io-client@4.8.1:
resolution: {integrity: sha512-hJVXfu3E28NmzGk8o1sHhN3om52tRvwYeidbj7xKy2eIIse5IoKX3USlS6Tqt3BHAtflLIkCQBkzVrEEfWUyYQ==}
engines: {node: '>=10.0.0'}
socket.io-parser@4.2.4:
resolution: {integrity: sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==}
engines: {node: '>=10.0.0'}
sonner@2.0.3: sonner@2.0.3:
resolution: {integrity: sha512-njQ4Hht92m0sMqqHVDL32V2Oun9W1+PHO9NDv9FHfJjT3JT22IG4Jpo3FPQy+mouRKCXFWO+r67v6MrHX2zeIA==} resolution: {integrity: sha512-njQ4Hht92m0sMqqHVDL32V2Oun9W1+PHO9NDv9FHfJjT3JT22IG4Jpo3FPQy+mouRKCXFWO+r67v6MrHX2zeIA==}
peerDependencies: peerDependencies:
@ -2896,6 +2938,22 @@ packages:
resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==}
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
ws@8.17.1:
resolution: {integrity: sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==}
engines: {node: '>=10.0.0'}
peerDependencies:
bufferutil: ^4.0.1
utf-8-validate: '>=5.0.2'
peerDependenciesMeta:
bufferutil:
optional: true
utf-8-validate:
optional: true
xmlhttprequest-ssl@2.1.2:
resolution: {integrity: sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ==}
engines: {node: '>=0.4.0'}
yocto-queue@0.1.0: yocto-queue@0.1.0:
resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
engines: {node: '>=10'} engines: {node: '>=10'}
@ -2903,6 +2961,9 @@ packages:
zod@3.24.3: zod@3.24.3:
resolution: {integrity: sha512-HhY1oqzWCQWuUqvBFnsyrtZRhyPeR7SUGv+C4+MsisMuVfSPx8HpwWqH8tRahSlt6M3PiFAcoeFhZAqIXTxoSg==} resolution: {integrity: sha512-HhY1oqzWCQWuUqvBFnsyrtZRhyPeR7SUGv+C4+MsisMuVfSPx8HpwWqH8tRahSlt6M3PiFAcoeFhZAqIXTxoSg==}
zod@4.2.1:
resolution: {integrity: sha512-0wZ1IRqGGhMP76gLqz8EyfBXKk0J2qo2+H3fi4mcUP/KtTocoX08nmIAHl1Z2kJIZbZee8KOpBCSNPRgauucjw==}
snapshots: snapshots:
'@alloc/quick-lru@5.2.0': {} '@alloc/quick-lru@5.2.0': {}
@ -3744,6 +3805,8 @@ snapshots:
'@rushstack/eslint-patch@1.11.0': {} '@rushstack/eslint-patch@1.11.0': {}
'@socket.io/component-emitter@3.1.2': {}
'@standard-schema/spec@1.0.0': {} '@standard-schema/spec@1.0.0': {}
'@standard-schema/utils@0.3.0': {} '@standard-schema/utils@0.3.0': {}
@ -3820,6 +3883,24 @@ snapshots:
postcss: 8.5.3 postcss: 8.5.3
tailwindcss: 4.1.4 tailwindcss: 4.1.4
'@taylordb/query-builder@0.10.1':
dependencies:
'@taylordb/shared': 0.4.4
eventemitter3: 5.0.1
fast-json-patch: 3.1.1
json-to-graphql-query: 2.3.0
lodash: 4.17.21
socket.io-client: 4.8.1
zod: 4.2.1
transitivePeerDependencies:
- bufferutil
- supports-color
- utf-8-validate
'@taylordb/shared@0.4.4':
dependencies:
lodash: 4.17.21
'@trivago/prettier-plugin-sort-imports@5.2.2(prettier@3.5.3)': '@trivago/prettier-plugin-sort-imports@5.2.2(prettier@3.5.3)':
dependencies: dependencies:
'@babel/generator': 7.27.0 '@babel/generator': 7.27.0
@ -4246,6 +4327,10 @@ snapshots:
dependencies: dependencies:
ms: 2.1.3 ms: 2.1.3
debug@4.3.7:
dependencies:
ms: 2.1.3
debug@4.4.0: debug@4.4.0:
dependencies: dependencies:
ms: 2.1.3 ms: 2.1.3
@ -4302,6 +4387,20 @@ snapshots:
emoji-regex@9.2.2: {} emoji-regex@9.2.2: {}
engine.io-client@6.6.3:
dependencies:
'@socket.io/component-emitter': 3.1.2
debug: 4.3.7
engine.io-parser: 5.2.3
ws: 8.17.1
xmlhttprequest-ssl: 2.1.2
transitivePeerDependencies:
- bufferutil
- supports-color
- utf-8-validate
engine.io-parser@5.2.3: {}
enhanced-resolve@5.18.1: enhanced-resolve@5.18.1:
dependencies: dependencies:
graceful-fs: 4.2.11 graceful-fs: 4.2.11
@ -4641,6 +4740,8 @@ snapshots:
esutils@2.0.3: {} esutils@2.0.3: {}
eventemitter3@5.0.1: {}
fast-deep-equal@3.1.3: {} fast-deep-equal@3.1.3: {}
fast-glob@3.3.1: fast-glob@3.3.1:
@ -4659,6 +4760,8 @@ snapshots:
merge2: 1.4.1 merge2: 1.4.1
micromatch: 4.0.8 micromatch: 4.0.8
fast-json-patch@3.1.1: {}
fast-json-stable-stringify@2.1.0: {} fast-json-stable-stringify@2.1.0: {}
fast-levenshtein@2.0.6: {} fast-levenshtein@2.0.6: {}
@ -5455,6 +5558,24 @@ snapshots:
is-arrayish: 0.3.2 is-arrayish: 0.3.2
optional: true optional: true
socket.io-client@4.8.1:
dependencies:
'@socket.io/component-emitter': 3.1.2
debug: 4.3.7
engine.io-client: 6.6.3
socket.io-parser: 4.2.4
transitivePeerDependencies:
- bufferutil
- supports-color
- utf-8-validate
socket.io-parser@4.2.4:
dependencies:
'@socket.io/component-emitter': 3.1.2
debug: 4.3.7
transitivePeerDependencies:
- supports-color
sonner@2.0.3(react-dom@19.1.0(react@19.1.0))(react@19.1.0): sonner@2.0.3(react-dom@19.1.0(react@19.1.0))(react@19.1.0):
dependencies: dependencies:
react: 19.1.0 react: 19.1.0
@ -5720,6 +5841,12 @@ snapshots:
word-wrap@1.2.5: {} word-wrap@1.2.5: {}
ws@8.17.1: {}
xmlhttprequest-ssl@2.1.2: {}
yocto-queue@0.1.0: {} yocto-queue@0.1.0: {}
zod@3.24.3: {} zod@3.24.3: {}
zod@4.2.1: {}

View File

@ -1,20 +1,15 @@
import AboutPage from '@/pages-templates/about'; import AboutPage from '@/pages-templates/about';
import { mainPageApi } from '@/features/pages/api/pages.api'; import { fetchAboutUsPageContent } from '@/features/pages/services/pages.service';
import { makeStore } from '@/shared/store';
export const metadata = { export const metadata = {
title: "О нас", title: 'О нас',
description: "Узнайте больше о нашей компании, истории и ценностях. Качественное топливо и отличный сервис.", description:
'Узнайте больше о нашей компании, истории и ценностях. Качественное топливо и отличный сервис.',
}; };
export default async function About() { export default async function About() {
const store = makeStore(); const data = await fetchAboutUsPageContent();
const { data } = await store.dispatch(
mainPageApi.endpoints.fetchAboutUsPageContent.initiate(),
);
if (!data) return null; if (!data) return null;

View File

@ -0,0 +1,238 @@
import { isEmpty } from 'lodash';
import {
AttachmentColumnValue,
TableRaws,
} from '@/shared/types/database.types';
// Helper to get image URL from Attachment array
export const getAttachmentUrl = (
attachments: AttachmentColumnValue[] | undefined | null,
): string | null => {
if (isEmpty(attachments) || !attachments?.[0]) return null;
const attachment = attachments[0];
return `${process.env.TAYLOR_MEDIA_URL}/${attachment.url}`;
};
// Helper to get link select name (link to selectTable returns object with name)
export const getLinkSelectName = (
link: { name: string } | undefined | null,
): string | null => {
return link?.name || null;
};
// Helper to get multiple link select names (for arrays of links)
export const getLinkSelectNames = (
links: Array<{ name: string }> | undefined | null,
): string[] => {
if (!links || isEmpty(links)) return [];
return links.map((link) => link.name);
};
// Presenters for TaylorDB query builder results (direct array format, no wrapper)
export const presentPartnersFromTaylor = (
partners: TableRaws<'partnyory'>[],
): Array<{ id: number; name: string; poster: string | null }> => {
return partners.map((partner, index) => ({
id: index + 1,
name: partner.nazvanie || '',
poster: getAttachmentUrl(partner.izobrozhenie),
}));
};
export const presentJobsFromTaylor = (
jobs: TableRaws<'vakansii'>[],
): Array<{
id: number;
name: string;
tags: string[];
location: string | null;
type: string | null;
}> => {
return jobs.map((job, index) => ({
id: index + 1,
name: job.zagolovok || '',
// tegi is a LinkColumnType, so it returns objects when loaded with .with()
tags: Array.isArray(job.tegi)
? (job.tegi as Array<{ name: string }>).map((tag) => tag.name)
: [],
// tip and lokaciya are SingleSelectColumnType, which return arrays of strings
location: job.lokaciya?.[0] || null,
type: job.tip?.[0] || null,
}));
};
export const presentDiscountsFromTaylor = (
discounts: TableRaws<'akcii'>[],
): Array<{
id: number;
name: string;
description: string;
expiresAt: string;
image: string | null;
}> => {
return discounts.map((discount, index) => ({
id: index + 1,
name: discount.zagolovok || '',
description: discount.opisanie || '',
expiresAt: discount.do || '',
image: getAttachmentUrl(discount.foto),
}));
};
export const presentStationsFromTaylor = (
stations: TableRaws<'azs'>[],
): Array<{
id: number;
name: string;
description: string;
address: string;
workingHours: string | null;
latitude: number;
longitude: number;
carWash: boolean;
ai92: boolean;
ai95: boolean;
dt: boolean;
z100: boolean;
propan: boolean;
electricCharge: boolean;
miniMarket: boolean;
toilet: boolean;
region: string | null;
image: string | null;
}> => {
return stations.map((station, index) => ({
id: index + 1,
name: station.imya || '',
description: station.opisanie || '',
address: station.adress || '',
// chasyRaboty and region are SingleSelectColumnType, which return arrays of strings
workingHours: station.chasyRaboty?.[0] || null,
// Parse string coordinates to numbers
latitude: parseFloat(station.lat || '0') || 0,
longitude: parseFloat(station.long || '0') || 0,
carWash: station.avtomojka || false,
ai92: station.ai92 || false,
ai95: station.ai95 || false,
dt: station.dt || false,
z100: station.z100 || false,
propan: station.propan || false,
electricCharge: station.zaryadnayaStanciya || false,
miniMarket: station.miniMarket || false,
toilet: station.tualet || false,
region: station.region?.[0] || null,
image: getAttachmentUrl(station.foto),
}));
};
export const presentTeamMembersFromTaylor = (
members: TableRaws<'komanda'>[],
): Array<{
name: string;
photo: string | null;
profession: string;
}> => {
return members.map((member) => ({
name: member.polnoeImya || '',
photo: getAttachmentUrl(member.foto),
profession: member.zvanie || '',
}));
};
export const presentHistoryItemsFromTaylor = (
historyItems: TableRaws<'istoriyaKompanii'>[],
): Array<{
name: string;
year: string;
description: string;
}> => {
return historyItems.map((item) => ({
name: item.zagolovok || '',
year: String(item.god || ''),
description: item.opisanie || '',
}));
};
export const presentReviewsFromTaylor = (
reviews: TableRaws<'otzyvy'>[],
): Array<{
id: number;
fullname: string;
review: string;
rating: number;
}> => {
return reviews.map((review) => ({
id: review.id || 0,
fullname: review.polnoeImya || '',
review: review.otzyv || '',
rating: review.rejting || 0,
}));
};
export const presentCharitiesFromTaylor = (
charities: TableRaws<'blagotvoritelnyjFond'>[],
): Array<{
id: number;
name: string;
description: string;
date: string;
location: string;
image: string | null;
}> => {
return charities.map((charity, index) => ({
id: index + 1,
name: charity.zagolovok || '',
description: charity.opisanie || '',
date: charity.data || '',
location: charity.lokaciya || '',
image: getAttachmentUrl(charity.foto),
}));
};
export const presentCertificatesFromTaylor = (
certificates: TableRaws<'sertifikaty'>[],
): Array<{
id: number;
name: string;
description: string;
issuedAt: string;
validUntil: string;
image: string | null;
}> => {
return certificates.map((certificate, index) => ({
id: index + 1,
name: certificate.nazvanie || '',
description: certificate.opisanie || '',
issuedAt: certificate.dataVydachi || '',
validUntil: certificate.dejstvitelenDo || '',
image: getAttachmentUrl(certificate.foto),
}));
};
export const presentTextsFromTaylor = (
texts: TableRaws<'tekstovyjKontentSajta'>[],
): Array<{
key: string;
value: string | null;
}> => {
return texts.map((item) => ({
key: item.klyuchNeIzmenyat || '',
value: item.znachenie || null,
}));
};
export const presentMediaFromTaylor = (
media: TableRaws<'mediaKontentSajta'>[],
): Array<{
key: string;
name: string;
photo: string | null;
}> => {
return media.map((record) => ({
key: record.klyuchNeIzmenyat || '',
name: record.mestopolozheniya || '',
photo: getAttachmentUrl(record.foto),
}));
};

View File

@ -1,13 +0,0 @@
import {
historyRequest,
reviewsRequest,
stationsWithImageRequest,
teamRequest,
} from './common';
export const aboutUsPageRequest = {
...teamRequest,
...historyRequest,
...stationsWithImageRequest,
...reviewsRequest,
};

View File

@ -1,5 +0,0 @@
import { certificatesRequest } from './common';
export const certificatesPageRequest = {
...certificatesRequest,
};

View File

@ -1,5 +0,0 @@
import { charityRequest } from './common';
export const charityPageRequest = {
...charityRequest,
};

View File

@ -1,13 +0,0 @@
import {
discountsRequest,
jobsRequest,
partnersRequest,
stationsRequest,
} from './common';
export const mainPageRequest = {
...partnersRequest,
...jobsRequest,
...discountsRequest,
...stationsRequest,
};

View File

@ -1,8 +1,6 @@
import { CharityPage } from '@/pages-templates/charity'; import { CharityPage } from '@/pages-templates/charity';
import { mainPageApi } from '@/features/pages/api/pages.api'; import { fetchCharityPageContent } from '@/features/pages/services/pages.service';
import { makeStore } from '@/shared/store';
export const metadata = { export const metadata = {
title: 'Благотворительность', title: 'Благотворительность',
@ -11,13 +9,9 @@ export const metadata = {
}; };
export default async function Charity() { export default async function Charity() {
const store = makeStore(); const data = await fetchCharityPageContent();
const { data, isLoading, error } = await store.dispatch( if (!data) return null;
mainPageApi.endpoints.fetchCharityPageContent.initiate(),
);
if (isLoading || !data) return null;
return <CharityPage content={data} />; return <CharityPage content={data} />;
} }

View File

@ -1,8 +1,6 @@
import { CertificatesPage } from '@/pages-templates/clients/certificates'; import { CertificatesPage } from '@/pages-templates/clients/certificates';
import { mainPageApi } from '@/features/pages/api/pages.api'; import { fetchCertificatesPageContent } from '@/features/pages/services/pages.service';
import { makeStore } from '@/shared/store';
export const metadata = { export const metadata = {
title: 'Сертификаты', title: 'Сертификаты',
@ -11,13 +9,9 @@ export const metadata = {
}; };
export default async function Certificates() { export default async function Certificates() {
const store = makeStore(); const data = await fetchCertificatesPageContent();
const { data, isLoading, error } = await store.dispatch( if (!data) return null;
mainPageApi.endpoints.fetchCertificatesPageContent.initiate(),
);
if (isLoading || !data) return null;
return <CertificatesPage content={data} />; return <CertificatesPage content={data} />;
} }

View File

@ -1,10 +1,12 @@
import type { Metadata } from 'next'; import type { Metadata } from 'next';
import { Inter } from 'next/font/google'; import { Inter } from 'next/font/google';
import { textControlApi } from '@/shared/language/api/text-control.api'; import {
import { mediaControlApi } from '@/shared/media/api/media-control.api'; fetchMediaContent,
fetchTextContent,
} from '@/features/pages/services/pages.service';
import { Providers } from '@/shared/providers/providers'; import { Providers } from '@/shared/providers/providers';
import { makeStore } from '@/shared/store';
import { MediaItem } from '@/shared/types/media.type'; import { MediaItem } from '@/shared/types/media.type';
import { TextItem } from '@/shared/types/text.types'; import { TextItem } from '@/shared/types/text.types';
@ -29,17 +31,11 @@ export default async function RootLayout({
}: Readonly<{ }: Readonly<{
children: React.ReactNode; children: React.ReactNode;
}>) { }>) {
const store = makeStore(); // Fetch texts and media using TaylorDB query builder
const [textItems, mediaItems] = await Promise.all([
// Запрос текстов fetchTextContent(),
const textResponse = await store.dispatch( fetchMediaContent(),
textControlApi.endpoints.fetchText.initiate(), ]);
);
// Запрос медиа
const mediaResponse = await store.dispatch(
mediaControlApi.endpoints.fetchMedia.initiate(),
);
return ( return (
<html <html
lang='ru' lang='ru'
@ -49,8 +45,8 @@ export default async function RootLayout({
> >
<body className={`${inter.className} min-w-2xs antialiased`}> <body className={`${inter.className} min-w-2xs antialiased`}>
<Providers <Providers
textItems={textResponse.data as TextItem[]} textItems={textItems as TextItem[]}
mediaItems={mediaResponse.data as MediaItem[]} mediaItems={mediaItems as MediaItem[]}
> >
<Header /> <Header />
{children} {children}

View File

@ -1,6 +1,4 @@
import { mainPageApi } from '@/features/pages/api/pages.api'; import { fetchMainPageContent } from '@/features/pages/services/pages.service';
import { makeStore } from '@/shared/store';
import { AboutSection } from '@/widgets/about-section'; import { AboutSection } from '@/widgets/about-section';
import { CharitySection } from '@/widgets/charity-section'; import { CharitySection } from '@/widgets/charity-section';
@ -13,13 +11,9 @@ import { StatsSection } from '@/widgets/stats-section';
import { VacanciesSection } from '@/widgets/vacancies-section'; import { VacanciesSection } from '@/widgets/vacancies-section';
export default async function Home() { export default async function Home() {
const store = makeStore(); const data = await fetchMainPageContent();
const { data, isLoading, error } = await store.dispatch( if (!data) return null;
mainPageApi.endpoints.fetchMainPageContent.initiate(),
);
if (isLoading || !data) return null;
return ( return (
<main> <main>

View File

@ -1,99 +0,0 @@
import { jsonToGraphQLQuery } from 'json-to-graphql-query';
import {
AboutUsPageData,
CertificatesPageData,
CharityPageData,
MainPageData,
} from '@/app/api-utlities/@types/pages';
import {
presentCertificates,
presentCharities,
presentDiscounts,
presentHistoryItems,
presentJobs,
presentPartners,
presentReviews,
presentStations,
presentTeamMembers,
} from '@/app/api-utlities/presenters';
import { aboutUsPageRequest } from '@/app/api-utlities/requests/about-us-page.request copy';
import { certificatesPageRequest } from '@/app/api-utlities/requests/certificates-page.request';
import { charityPageRequest } from '@/app/api-utlities/requests/charity-page.request copy';
import { mainPageRequest } from '@/app/api-utlities/requests/main-page.request';
import { taylorAPI } from '@/shared/api/taylor-api';
export const mainPageApi = taylorAPI.injectEndpoints({
endpoints: (builder) => ({
fetchMainPageContent: builder.query<MainPageData, void>({
query: () => ({
url: '',
method: 'POST',
body: {
query: jsonToGraphQLQuery({ query: mainPageRequest }),
},
}),
transformResponse: (response: any) => {
return {
partners: presentPartners(response.data.partnyory),
jobs: presentJobs(response.data.vakansii),
discounts: presentDiscounts(response.data.akcii),
stations: presentStations(response.data.azs),
};
},
}),
fetchAboutUsPageContent: builder.query<AboutUsPageData, void>({
query: () => ({
url: '',
method: 'POST',
body: {
query: jsonToGraphQLQuery({ query: aboutUsPageRequest }),
},
}),
transformResponse: (response: any) => {
return {
team: presentTeamMembers(response.data.komanda),
history: presentHistoryItems(response.data.istoriyaKompanii),
stations: presentStations(response.data.azs),
reviews: presentReviews(response.data.otzyvy),
};
},
}),
fetchCharityPageContent: builder.query<CharityPageData, void>({
query: () => ({
url: '',
method: 'POST',
body: {
query: jsonToGraphQLQuery({ query: charityPageRequest }),
},
}),
transformResponse: (response: any) => {
return {
charities: presentCharities(response.data.blagotvoritelnyjFond),
};
},
}),
fetchCertificatesPageContent: builder.query<CertificatesPageData, void>({
query: () => ({
url: '',
method: 'POST',
body: {
query: jsonToGraphQLQuery({ query: certificatesPageRequest }),
},
}),
transformResponse: (response: any) => {
return {
certificates: presentCertificates(response.data.sertifikaty),
};
},
}),
}),
});

View File

@ -0,0 +1,182 @@
import {
AboutUsPageData,
CertificatesPageData,
CharityPageData,
MainPageData,
} from '@/app/api-utlities/@types/pages';
import {
presentCertificatesFromTaylor,
presentCharitiesFromTaylor,
presentDiscountsFromTaylor,
presentHistoryItemsFromTaylor,
presentJobsFromTaylor,
presentMediaFromTaylor,
presentPartnersFromTaylor,
presentReviewsFromTaylor,
presentStationsFromTaylor,
presentTeamMembersFromTaylor,
presentTextsFromTaylor,
} from '@/app/api-utlities/presenters/taylor-presenters';
import { taylorQueryBuilder } from '@/shared/api/taylor-query-builder';
/**
* Fetches main page content using TaylorDB query builder
* Replaces the RTK Query GraphQL approach with type-safe query builder
*/
export async function fetchMainPageContent(): Promise<MainPageData> {
// Use batch queries to fetch all data in a single request
const [partnersData, jobsData, discountsData, stationsData] =
await taylorQueryBuilder
.batch([
// Fetch partners
taylorQueryBuilder
.selectFrom('partnyory')
.selectAll()
.with({
izobrozhenie: (qb) => qb.selectAll(),
}),
// Fetch jobs
taylorQueryBuilder
.selectFrom('vakansii')
.selectAll()
.with({
tegi: (qb) => qb.select(['name']),
}),
// Fetch discounts
taylorQueryBuilder
.selectFrom('akcii')
.selectAll()
.with({
foto: (qb) => qb.selectAll(),
}),
// Fetch stations
taylorQueryBuilder
.selectFrom('azs')
.selectAll()
.with({
foto: (qb) => qb.selectAll(),
}),
])
.execute();
// Transform the data using TaylorDB-specific presenters
// The query builder returns arrays directly
return {
partners: presentPartnersFromTaylor(partnersData),
jobs: presentJobsFromTaylor(jobsData),
discounts: presentDiscountsFromTaylor(discountsData),
stations: presentStationsFromTaylor(stationsData),
};
}
/**
* Fetches about us page content using TaylorDB query builder
*/
export async function fetchAboutUsPageContent(): Promise<AboutUsPageData> {
// Use batch queries to fetch all data in a single request
const [teamData, historyData, stationsData, reviewsData] =
await taylorQueryBuilder
.batch([
// Fetch team members
taylorQueryBuilder
.selectFrom('komanda')
.selectAll()
.with({
foto: (qb) => qb.selectAll(),
}),
// Fetch history items
taylorQueryBuilder.selectFrom('istoriyaKompanii').selectAll(),
// Fetch stations
taylorQueryBuilder
.selectFrom('azs')
.selectAll()
.with({
foto: (qb) => qb.selectAll(),
}),
// Fetch reviews (filtered by published status)
taylorQueryBuilder
.selectFrom('otzyvy')
.selectAll()
.where('status', '=', 'Опубликовано'),
])
.execute();
return {
team: presentTeamMembersFromTaylor(teamData),
history: presentHistoryItemsFromTaylor(historyData),
stations: presentStationsFromTaylor(stationsData),
reviews: presentReviewsFromTaylor(reviewsData),
};
}
/**
* Fetches charity page content using TaylorDB query builder
*/
export async function fetchCharityPageContent(): Promise<CharityPageData> {
const charitiesData = await taylorQueryBuilder
.selectFrom('blagotvoritelnyjFond')
.selectAll()
.with({
foto: (qb) => qb.selectAll(),
})
.execute();
return {
charities: presentCharitiesFromTaylor(charitiesData),
};
}
/**
* Fetches certificates page content using TaylorDB query builder
*/
export async function fetchCertificatesPageContent(): Promise<CertificatesPageData> {
const certificatesData = await taylorQueryBuilder
.selectFrom('sertifikaty')
.selectAll()
.with({
foto: (qb) => qb.selectAll(),
})
.execute();
return {
certificates: presentCertificatesFromTaylor(certificatesData),
};
}
/**
* Fetches text content using TaylorDB query builder
*/
export async function fetchTextContent(): Promise<
Array<{ key: string; value: string | null }>
> {
const textsData = await taylorQueryBuilder
.selectFrom('tekstovyjKontentSajta')
.selectAll()
.execute();
return presentTextsFromTaylor(textsData);
}
/**
* Fetches media content using TaylorDB query builder
*/
export async function fetchMediaContent(): Promise<
Array<{ key: string; name: string; photo: string | null }>
> {
const mediaData = await taylorQueryBuilder
.selectFrom('mediaKontentSajta')
.selectAll()
.with({
foto: (qb) => qb.selectAll(),
})
.execute();
return presentMediaFromTaylor(mediaData);
}

View File

@ -0,0 +1,13 @@
import { createQueryBuilder } from '@taylordb/query-builder';
import { TaylorDatabase } from '../types/database.types';
// Initialize TaylorDB query builder instance
// Note: If you have generated types from taylor.types.ts, you can import them here
// import { TaylorDatabase } from '@/path/to/taylor.types';
export const taylorQueryBuilder = createQueryBuilder<TaylorDatabase>({
baseId: process.env.TAYLOR_BASE_ID || '',
baseUrl: process.env.TAYLOR_API_ENDPOINT || '',
apiKey: process.env.TAYLOR_API_TOKEN || '',
});

View File

@ -1,25 +0,0 @@
import { jsonToGraphQLQuery } from 'json-to-graphql-query';
import { presentTexts } from '@/app/api-utlities/presenters';
import { textsRequest } from '@/app/api-utlities/requests/common';
import { taylorAPI } from '@/shared/api/taylor-api';
import { TextItem } from '@/shared/types/text.types';
export const textControlApi = taylorAPI.injectEndpoints({
endpoints: (builder) => ({
fetchText: builder.query<TextItem[], void>({
query: () => ({
url: '',
method: 'POST',
body: {
query: jsonToGraphQLQuery({ query: textsRequest }),
},
}),
transformResponse: (response: any) => {
return presentTexts(response.data.tekstovyjKontentSajta);
},
}),
}),
});

View File

@ -0,0 +1,548 @@
/**
* Copyright (c) 2025 TaylorDB
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
interface FileInformation {
fieldname: string;
originalname: string;
encoding: string;
mimetype: string;
destination: string;
filename: string;
path: string;
size: number;
format: string;
width: number;
height: number;
}
interface UploadResponse {
collectionName: string;
fileInformation: FileInformation;
metadata: {
thumbnails: any[];
clips: any[];
};
baseId: string;
storageAdaptor: string;
_id: string;
__v: number;
}
export interface AttachmentColumnValue {
url: string;
fileType: string;
size: number;
}
export class Attachment {
public readonly collectionName: string;
public readonly fileInformation: FileInformation;
public readonly metadata: { thumbnails: any[]; clips: any[] };
public readonly baseId: string;
public readonly storageAdaptor: string;
public readonly _id: string;
constructor(data: UploadResponse) {
this.collectionName = data.collectionName;
this.fileInformation = data.fileInformation;
this.metadata = data.metadata;
this.baseId = data.baseId;
this.storageAdaptor = data.storageAdaptor;
this._id = data._id;
}
toColumnValue(): AttachmentColumnValue {
return {
url: this.fileInformation.path,
fileType: this.fileInformation.mimetype,
size: this.fileInformation.size,
};
}
}
type IsWithinOperatorValue =
| 'pastWeek'
| 'pastMonth'
| 'pastYear'
| 'nextWeek'
| 'nextMonth'
| 'nextYear'
| 'daysFromNow'
| 'daysAgo'
| 'currentWeek'
| 'currentMonth'
| 'currentYear';
type DefaultDateFilterValue =
| (
| 'today'
| 'tomorrow'
| 'yesterday'
| 'oneWeekAgo'
| 'oneWeekFromNow'
| 'oneMonthAgo'
| 'oneMonthFromNow'
)
| ['exactDay' | 'exactTimestamp', string]
| ['daysAgo' | 'daysFromNow', number];
type DateFilters = {
'=': DefaultDateFilterValue;
'!=': DefaultDateFilterValue;
'<': DefaultDateFilterValue;
'>': DefaultDateFilterValue;
'<=': DefaultDateFilterValue;
'>=': DefaultDateFilterValue;
isWithIn:
| IsWithinOperatorValue
| { value: 'daysAgo' | 'daysFromNow'; date: number };
isEmpty: boolean;
isNotEmpty: boolean;
};
type DateAggregations = {
empty: number;
filled: number;
unique: number;
percentEmpty: number;
percentFilled: number;
percentUnique: number;
min: number | null;
max: number | null;
daysRange: number | null;
monthRange: number | null;
};
type TextFilters = {
'=': string;
'!=': string;
caseEqual: string;
hasAnyOf: string[];
contains: string;
startsWith: string;
endsWith: string;
doesNotContain: string;
isEmpty: never;
isNotEmpty: never;
};
type LinkFilters = {
hasAnyOf: number[];
hasAllOf: number[];
isExactly: number[];
'=': number;
hasNoneOf: number[];
contains: string;
doesNotContain: string;
isEmpty: never;
isNotEmpty: never;
};
type SelectFilters<O extends readonly string[]> = {
hasAnyOf: O[number][];
hasAllOf: O[number][];
isExactly: O[number][];
'=': O[number];
hasNoneOf: O[number][];
contains: string;
doesNotContain: string;
isEmpty: never;
isNotEmpty: never;
};
type LinkAggregations = {
empty: number;
filled: number;
percentEmpty: number;
percentFilled: number;
};
type NumberFilters = {
'=': number;
'!=': number;
'>': number;
'>=': number;
'<': number;
'<=': number;
hasAnyOf: number[];
hasNoneOf: number[];
isEmpty: never;
isNotEmpty: never;
};
type NumberAggregations = {
sum: number;
average: number;
median: number;
min: number | null;
max: number | null;
range: number;
standardDeviation: number;
histogram: Record<string, number>;
empty: number;
filled: number;
unique: number;
percentEmpty: number;
percentFilled: number;
percentUnique: number;
};
type CheckboxFilters = {
'=': number;
};
/**
*
* Column types
*
*/
export type ColumnType<
S,
U,
I,
R extends boolean,
F extends { [key: string]: any } = object,
A extends { [key: string]: any } = object,
> = {
raw: S;
insert: I;
update: U;
filters: F;
aggregations: A;
isRequired: R;
};
export type DateColumnType<R extends boolean> = ColumnType<
string,
string,
string,
R,
DateFilters,
DateAggregations
>;
export type TextColumnType<R extends boolean> = ColumnType<
string,
string,
string,
R,
TextFilters
>;
export type ALinkColumnType<
T extends string,
S,
U,
I,
R extends boolean,
F extends { [key: string]: any } = LinkFilters,
A extends LinkAggregations = LinkAggregations,
> = ColumnType<S, U, I, R, F, A> & {
linkedTo: T;
};
export type LinkColumnType<
T extends string,
R extends boolean,
> = ALinkColumnType<
T,
object,
number | number[] | { newIds: number[]; deletedIds: number[] },
number | number[],
R
>;
export type AttachmentColumnType<R extends boolean> = ALinkColumnType<
'attachmentTable',
AttachmentColumnValue[],
Attachment[] | { newIds: number[]; deletedIds: number[] } | number[],
Attachment[] | number[],
R
>;
export type NumberColumnType<R extends boolean> = ColumnType<
number,
number,
number,
R,
NumberFilters,
NumberAggregations
>;
export type CheckboxColumnType<R extends boolean> = ColumnType<
boolean,
boolean,
boolean,
R,
CheckboxFilters
>;
export type AutoGeneratedNumberColumnType = ColumnType<
number,
never,
never,
false,
NumberFilters,
NumberAggregations
>;
export type AutoGeneratedDateColumnType = ColumnType<
string,
never,
never,
false,
DateFilters,
DateAggregations
>;
export type SingleSelectColumnType<
O extends readonly string[],
R extends boolean,
> = ALinkColumnType<
'selectTable',
O[number],
O[number] | O[number][],
O[number] | O[number][],
R,
SelectFilters<O>
>;
export type TableRaws<T extends keyof TaylorDatabase> = {
[K in keyof TaylorDatabase[T]]: TaylorDatabase[T][K] extends ColumnType<
infer S,
any,
any,
infer R,
any,
any
>
? R extends true
? S
: S | undefined
: never;
};
export type TableInserts<T extends keyof TaylorDatabase> = {
[K in keyof TaylorDatabase[T]]: TaylorDatabase[T][K] extends ColumnType<
any,
infer I,
any,
infer R,
any,
any
>
? R extends true
? I
: I | undefined
: never;
};
export type TableUpdates<T extends keyof TaylorDatabase> = {
[K in keyof TaylorDatabase[T]]: TaylorDatabase[T][K] extends ColumnType<
any,
any,
infer U,
any,
any,
any
>
? U
: never;
};
export type SelectTable = {
id: AutoGeneratedNumberColumnType;
name: TextColumnType<true>;
color: TextColumnType<true>;
};
export type AttachmentTable = {
id: AutoGeneratedNumberColumnType;
name: TextColumnType<true>;
metadata: TextColumnType<true>;
size: NumberColumnType<true>;
fileType: TextColumnType<true>;
url: TextColumnType<true>;
};
export type CollaboratorsTable = {
id: AutoGeneratedNumberColumnType;
name: TextColumnType<true>;
emailAddress: TextColumnType<true>;
avatar: TextColumnType<true>;
};
export type TaylorDatabase = {
/**
*
*
* Internal tables, these tables can not be queried directly.
*
*/
selectTable: SelectTable;
attachmentTable: AttachmentTable;
collaboratorsTable: CollaboratorsTable;
vakansii: VakansiiTable;
partnyory: PartnyoryTable;
azs: AzsTable;
akcii: AkciiTable;
istoriyaKompanii: IstoriyaKompaniiTable;
komanda: KomandaTable;
otzyvy: OtzyvyTable;
tekstovyjKontentSajta: TekstovyjKontentSajtaTable;
sertifikaty: SertifikatyTable;
mediaKontentSajta: MediaKontentSajtaTable;
blagotvoritelnyjFond: BlagotvoritelnyjFondTable;
};
export const VakansiiTipOptions = ['Офис', 'Заправки'] as const;
export const VakansiiLokaciyaOptions = ['Душанбе'] as const;
type VakansiiTable = {
id: NumberColumnType<false>;
createdAt: AutoGeneratedDateColumnType;
updatedAt: AutoGeneratedDateColumnType;
zagolovok: TextColumnType<false>;
tip: SingleSelectColumnType<typeof VakansiiTipOptions, false>;
lokaciya: SingleSelectColumnType<typeof VakansiiLokaciyaOptions, false>;
tegi: LinkColumnType<'selectTable', false>;
};
type PartnyoryTable = {
id: NumberColumnType<false>;
createdAt: AutoGeneratedDateColumnType;
updatedAt: AutoGeneratedDateColumnType;
nazvanie: TextColumnType<false>;
izobrozhenie: AttachmentColumnType<false>;
};
export const AzsChasyRabotyOptions = ['Круглосуточно'] as const;
export const AzsRegionOptions = [
'Душанбе',
'Бохтар',
'Худжанд',
'Регар',
'Вахдат',
'А.Джоми',
'Обикиик',
'Кулоб',
'Дахана',
'Ёвон',
'Панч',
'Исфара',
'Мастчох',
'Хисор',
] as const;
type AzsTable = {
id: NumberColumnType<false>;
createdAt: AutoGeneratedDateColumnType;
updatedAt: AutoGeneratedDateColumnType;
imya: TextColumnType<false>;
adress: TextColumnType<false>;
opisanie: TextColumnType<false>;
chasyRaboty: SingleSelectColumnType<typeof AzsChasyRabotyOptions, false>;
lat: TextColumnType<false>;
long: TextColumnType<false>;
avtomojka: CheckboxColumnType<false>;
dt: CheckboxColumnType<false>;
ai92: CheckboxColumnType<false>;
ai95: CheckboxColumnType<false>;
z100: CheckboxColumnType<false>;
propan: CheckboxColumnType<false>;
zaryadnayaStanciya: CheckboxColumnType<false>;
miniMarket: CheckboxColumnType<false>;
tualet: CheckboxColumnType<false>;
region: SingleSelectColumnType<typeof AzsRegionOptions, false>;
foto: AttachmentColumnType<false>;
};
type AkciiTable = {
id: NumberColumnType<false>;
createdAt: AutoGeneratedDateColumnType;
updatedAt: AutoGeneratedDateColumnType;
zagolovok: TextColumnType<false>;
opisanie: TextColumnType<false>;
do: DateColumnType<false>;
foto: AttachmentColumnType<false>;
};
type IstoriyaKompaniiTable = {
id: NumberColumnType<false>;
createdAt: AutoGeneratedDateColumnType;
updatedAt: AutoGeneratedDateColumnType;
zagolovok: TextColumnType<false>;
god: NumberColumnType<false>;
opisanie: TextColumnType<false>;
};
type KomandaTable = {
id: NumberColumnType<false>;
createdAt: AutoGeneratedDateColumnType;
updatedAt: AutoGeneratedDateColumnType;
polnoeImya: TextColumnType<false>;
foto: AttachmentColumnType<false>;
zvanie: TextColumnType<false>;
};
export const OtzyvyStatusOptions = ['Опубликовано'] as const;
type OtzyvyTable = {
id: NumberColumnType<false>;
createdAt: AutoGeneratedDateColumnType;
updatedAt: AutoGeneratedDateColumnType;
polnoeImya: TextColumnType<false>;
otzyv: TextColumnType<false>;
rejting: NumberColumnType<false>;
status: SingleSelectColumnType<typeof OtzyvyStatusOptions, false>;
};
type TekstovyjKontentSajtaTable = {
id: NumberColumnType<false>;
createdAt: AutoGeneratedDateColumnType;
updatedAt: AutoGeneratedDateColumnType;
klyuchNeIzmenyat: TextColumnType<true>;
znachenie: TextColumnType<true>;
opisanie: LinkColumnType<'selectTable', false>;
};
type SertifikatyTable = {
id: NumberColumnType<false>;
createdAt: AutoGeneratedDateColumnType;
updatedAt: AutoGeneratedDateColumnType;
nazvanie: TextColumnType<false>;
opisanie: TextColumnType<false>;
dataVydachi: DateColumnType<false>;
dejstvitelenDo: DateColumnType<false>;
foto: AttachmentColumnType<false>;
};
export const MediaKontentSajtaStranicaOptions = [
'Главная',
'О нас',
'Благотворительность',
'Общая',
'Клиенты',
'Программа лояльности',
] as const;
type MediaKontentSajtaTable = {
id: NumberColumnType<false>;
createdAt: AutoGeneratedDateColumnType;
updatedAt: AutoGeneratedDateColumnType;
mestopolozheniya: TextColumnType<false>;
klyuchNeIzmenyat: TextColumnType<true>;
foto: AttachmentColumnType<false>;
stranica: SingleSelectColumnType<
typeof MediaKontentSajtaStranicaOptions,
false
>;
};
type BlagotvoritelnyjFondTable = {
id: NumberColumnType<false>;
createdAt: AutoGeneratedDateColumnType;
updatedAt: AutoGeneratedDateColumnType;
zagolovok: TextColumnType<false>;
opisanie: TextColumnType<false>;
data: DateColumnType<false>;
lokaciya: TextColumnType<false>;
foto: AttachmentColumnType<false>;
};