feat: add graphql

This commit is contained in:
Zotov Ivan Yuryevich
2026-04-05 23:47:29 +03:00
parent 4b196e3d0e
commit 8b100c18b7
13 changed files with 6246 additions and 1 deletions

3
.vscode/extensions.json vendored Normal file
View File

@@ -0,0 +1,3 @@
{
"recommendations": ["GraphQL.vscode-graphql"]
}

35
graphql.config.yml Normal file
View File

@@ -0,0 +1,35 @@
# GraphQL Config: one file for the IDE (vscode-graphql) and GraphQL Code Generator.
# - default: IntelliSense + generated TS (uses committed schema.graphql).
# - introspect: refresh schema.graphql from Strapi (needs Strapi up): npm run graphql:schema
projects:
default:
schema: ./schema.graphql
documents: ./src/**/*.gql.ts
extensions:
codegen:
ignoreNoDocuments: true
generates:
# Same as client-preset graphql.ts output, without gql.ts (gql-tag map / gen-dts).
src/graphql/graphql.ts:
plugins:
- add:
content: "/* eslint-disable */"
- typescript:
inputMaybeValue: T | null | undefined
- typescript-operations: {}
- typed-document-node: {}
config:
useTypeImports: true
documentMode: string
introspect:
schema: http://localhost:1337/graphql
documents: ./src/**/*.gql.ts
extensions:
codegen:
ignoreNoDocuments: true
generates:
./schema.graphql:
plugins:
- schema-ast

3547
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -11,7 +11,10 @@
"dev": "astro dev",
"build": "astro build",
"preview": "astro preview",
"types:generate": "openapi-typescript http://localhost:1337/swagger-spec.json -o ./src/api/paths.ts"
"types:generate": "openapi-typescript http://localhost:1337/swagger-spec.json -o ./src/api/paths.ts",
"graphql:generate": "graphql-codegen",
"graphql:schema": "graphql-codegen -p introspect",
"endpoint:generate": "node scripts/endpoint-generate.mjs"
},
"dependencies": {
"@astrojs/react": "^4.4.2",
@@ -46,6 +49,8 @@
"clsx": "^2.1.1",
"cmdk": "^1.1.1",
"embla-carousel-react": "^8.6.0",
"graphql": "^16.13.2",
"graphql-tag": "^2.12.6",
"input-otp": "^1.4.2",
"lucide-react": "^0.487.0",
"next-themes": "^0.4.6",
@@ -62,6 +67,12 @@
"vaul": "^1.1.2"
},
"devDependencies": {
"@graphql-codegen/add": "^6.0.0",
"@graphql-codegen/cli": "^6.2.1",
"@graphql-codegen/schema-ast": "^5.0.1",
"@graphql-codegen/typed-document-node": "^6.1.7",
"@graphql-codegen/typescript": "^5.0.9",
"@graphql-codegen/typescript-operations": "^5.0.9",
"@tailwindcss/vite": "^4.1.12",
"@types/node": "^20.11.19",
"@types/react": "^18.3.12",

904
schema.graphql Normal file
View File

@@ -0,0 +1,904 @@
input BooleanFilterInput {
and: [Boolean]
between: [Boolean]
contains: Boolean
containsi: Boolean
endsWith: Boolean
eq: Boolean
eqi: Boolean
gt: Boolean
gte: Boolean
in: [Boolean]
lt: Boolean
lte: Boolean
ne: Boolean
nei: Boolean
not: BooleanFilterInput
notContains: Boolean
notContainsi: Boolean
notIn: [Boolean]
notNull: Boolean
null: Boolean
or: [Boolean]
startsWith: Boolean
}
type Category {
badge: String
createdAt: DateTime
description: String
documentId: ID!
group: ENUM_CATEGORY_GROUP!
publishedAt: DateTime
slug: String!
sortOrder: Int!
title: String
updatedAt: DateTime
vendors(filters: VendorFiltersInput, pagination: PaginationArg = {}, sort: [String] = []): [Vendor]!
vendors_connection(filters: VendorFiltersInput, pagination: PaginationArg = {}, sort: [String] = []): VendorRelationResponseCollection
}
type CategoryEntityResponseCollection {
nodes: [Category!]!
pageInfo: Pagination!
}
input CategoryFiltersInput {
and: [CategoryFiltersInput]
badge: StringFilterInput
createdAt: DateTimeFilterInput
description: StringFilterInput
documentId: IDFilterInput
group: StringFilterInput
not: CategoryFiltersInput
or: [CategoryFiltersInput]
publishedAt: DateTimeFilterInput
slug: StringFilterInput
sortOrder: IntFilterInput
title: StringFilterInput
updatedAt: DateTimeFilterInput
vendors: VendorFiltersInput
}
input CategoryInput {
badge: String
description: String
group: ENUM_CATEGORY_GROUP
publishedAt: DateTime
slug: String
sortOrder: Int
title: String
vendors: [ID]
}
type CategoryRelationResponseCollection {
nodes: [Category!]!
}
type ComponentCardsService {
category: Category
color: ComponentSharedColor
description: String
icon: ComponentSharedIcon
id: ID!
title: String
}
input ComponentCardsServiceFiltersInput {
and: [ComponentCardsServiceFiltersInput]
category: CategoryFiltersInput
color: ComponentSharedColorFiltersInput
description: StringFilterInput
icon: ComponentSharedIconFiltersInput
not: ComponentCardsServiceFiltersInput
or: [ComponentCardsServiceFiltersInput]
title: StringFilterInput
}
type ComponentSectionsHomeHero {
badge: String
description: String
id: ID!
stats(filters: ComponentSharedAttributeFiltersInput, pagination: PaginationArg = {}, sort: [String] = []): [ComponentSharedAttribute]
title: String
}
type ComponentSectionsServices {
cards(filters: ComponentCardsServiceFiltersInput, pagination: PaginationArg = {}, sort: [String] = []): [ComponentCardsService]
description: String
id: ID!
title: String
}
type ComponentSharedAttribute {
id: ID!
key: String
value: String
}
input ComponentSharedAttributeFiltersInput {
and: [ComponentSharedAttributeFiltersInput]
key: StringFilterInput
not: ComponentSharedAttributeFiltersInput
or: [ComponentSharedAttributeFiltersInput]
value: StringFilterInput
}
type ComponentSharedColor {
id: ID!
value: ENUM_COMPONENTSHAREDCOLOR_VALUE
}
input ComponentSharedColorFiltersInput {
and: [ComponentSharedColorFiltersInput]
not: ComponentSharedColorFiltersInput
or: [ComponentSharedColorFiltersInput]
value: StringFilterInput
}
type ComponentSharedContact {
displayValue: String
id: ID!
type: ENUM_COMPONENTSHAREDCONTACT_TYPE!
value: String!
}
type ComponentSharedIcon {
id: ID!
value: ENUM_COMPONENTSHAREDICON_VALUE
}
input ComponentSharedIconFiltersInput {
and: [ComponentSharedIconFiltersInput]
not: ComponentSharedIconFiltersInput
or: [ComponentSharedIconFiltersInput]
value: StringFilterInput
}
type ComponentSharedString {
id: ID!
value: String!
}
input ComponentSharedStringFiltersInput {
and: [ComponentSharedStringFiltersInput]
not: ComponentSharedStringFiltersInput
or: [ComponentSharedStringFiltersInput]
value: StringFilterInput
}
input ComponentSharedStringInput {
id: ID
value: String
}
"""
A date-time string at UTC, such as 2007-12-03T10:15:30Z, compliant with the `date-time` format outlined in section 5.6 of the RFC 3339 profile of the ISO 8601 standard for representation of dates and times using the Gregorian calendar.
"""
scalar DateTime
input DateTimeFilterInput {
and: [DateTime]
between: [DateTime]
contains: DateTime
containsi: DateTime
endsWith: DateTime
eq: DateTime
eqi: DateTime
gt: DateTime
gte: DateTime
in: [DateTime]
lt: DateTime
lte: DateTime
ne: DateTime
nei: DateTime
not: DateTimeFilterInput
notContains: DateTime
notContainsi: DateTime
notIn: [DateTime]
notNull: Boolean
null: Boolean
or: [DateTime]
startsWith: DateTime
}
type DeleteMutationResponse {
documentId: ID!
}
enum ENUM_CATEGORY_GROUP {
service
solution
}
enum ENUM_COMPONENTSHAREDCOLOR_VALUE {
blue
cyan
emerald
indigo
orange
pink
violet
}
enum ENUM_COMPONENTSHAREDCONTACT_TYPE {
address
email
phone
working_hours
}
enum ENUM_COMPONENTSHAREDICON_VALUE {
award
building
check_circle
cloud
cpu
database
factory
graduation_cap
heart
landmark
package
rocket
server
settings
shield
shopping_cart
users
zap
}
type Error {
code: String!
message: String
}
input FileInfoInput {
alternativeText: String
caption: String
name: String
}
input FloatFilterInput {
and: [Float]
between: [Float]
contains: Float
containsi: Float
endsWith: Float
eq: Float
eqi: Float
gt: Float
gte: Float
in: [Float]
lt: Float
lte: Float
ne: Float
nei: Float
not: FloatFilterInput
notContains: Float
notContainsi: Float
notIn: [Float]
notNull: Boolean
null: Boolean
or: [Float]
startsWith: Float
}
union GenericMorph = Category | ComponentCardsService | ComponentSectionsHomeHero | ComponentSectionsServices | ComponentSharedAttribute | ComponentSharedColor | ComponentSharedContact | ComponentSharedIcon | ComponentSharedString | HomePage | I18NLocale | ReviewWorkflowsWorkflow | ReviewWorkflowsWorkflowStage | SiteConfig | UploadFile | UsersPermissionsPermission | UsersPermissionsRole | UsersPermissionsUser | Vendor
type HomePage {
createdAt: DateTime
documentId: ID!
publishedAt: DateTime
sections: [HomePageSectionsDynamicZone]
updatedAt: DateTime
}
input HomePageInput {
publishedAt: DateTime
sections: [HomePageSectionsDynamicZoneInput!]
}
union HomePageSectionsDynamicZone = ComponentSectionsHomeHero | ComponentSectionsServices | Error
scalar HomePageSectionsDynamicZoneInput
type I18NLocale {
code: String
createdAt: DateTime
documentId: ID!
name: String
publishedAt: DateTime
updatedAt: DateTime
}
type I18NLocaleEntityResponseCollection {
nodes: [I18NLocale!]!
pageInfo: Pagination!
}
input I18NLocaleFiltersInput {
and: [I18NLocaleFiltersInput]
code: StringFilterInput
createdAt: DateTimeFilterInput
documentId: IDFilterInput
name: StringFilterInput
not: I18NLocaleFiltersInput
or: [I18NLocaleFiltersInput]
publishedAt: DateTimeFilterInput
updatedAt: DateTimeFilterInput
}
input IDFilterInput {
and: [ID]
between: [ID]
contains: ID
containsi: ID
endsWith: ID
eq: ID
eqi: ID
gt: ID
gte: ID
in: [ID]
lt: ID
lte: ID
ne: ID
nei: ID
not: IDFilterInput
notContains: ID
notContainsi: ID
notIn: [ID]
notNull: Boolean
null: Boolean
or: [ID]
startsWith: ID
}
input IntFilterInput {
and: [Int]
between: [Int]
contains: Int
containsi: Int
endsWith: Int
eq: Int
eqi: Int
gt: Int
gte: Int
in: [Int]
lt: Int
lte: Int
ne: Int
nei: Int
not: IntFilterInput
notContains: Int
notContainsi: Int
notIn: [Int]
notNull: Boolean
null: Boolean
or: [Int]
startsWith: Int
}
"""
The `JSON` scalar type represents JSON values as specified by [ECMA-404](http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf).
"""
scalar JSON
input JSONFilterInput {
and: [JSON]
between: [JSON]
contains: JSON
containsi: JSON
endsWith: JSON
eq: JSON
eqi: JSON
gt: JSON
gte: JSON
in: [JSON]
lt: JSON
lte: JSON
ne: JSON
nei: JSON
not: JSONFilterInput
notContains: JSON
notContainsi: JSON
notIn: [JSON]
notNull: Boolean
null: Boolean
or: [JSON]
startsWith: JSON
}
type Mutation {
"""Change user password. Confirm with the current password."""
changePassword(currentPassword: String!, password: String!, passwordConfirmation: String!): UsersPermissionsLoginPayload
createCategory(data: CategoryInput!, status: PublicationStatus = PUBLISHED): Category
createReviewWorkflowsWorkflow(data: ReviewWorkflowsWorkflowInput!, status: PublicationStatus = PUBLISHED): ReviewWorkflowsWorkflow
createReviewWorkflowsWorkflowStage(data: ReviewWorkflowsWorkflowStageInput!, status: PublicationStatus = PUBLISHED): ReviewWorkflowsWorkflowStage
"""Create a new role"""
createUsersPermissionsRole(data: UsersPermissionsRoleInput!): UsersPermissionsCreateRolePayload
"""Create a new user"""
createUsersPermissionsUser(data: UsersPermissionsUserInput!): UsersPermissionsUserEntityResponse!
createVendor(data: VendorInput!, status: PublicationStatus = PUBLISHED): Vendor
deleteCategory(documentId: ID!): DeleteMutationResponse
deleteHomePage: DeleteMutationResponse
deleteReviewWorkflowsWorkflow(documentId: ID!): DeleteMutationResponse
deleteReviewWorkflowsWorkflowStage(documentId: ID!): DeleteMutationResponse
deleteSiteConfig: DeleteMutationResponse
deleteUploadFile(id: ID!): UploadFile
"""Delete an existing role"""
deleteUsersPermissionsRole(id: ID!): UsersPermissionsDeleteRolePayload
"""Delete an existing user"""
deleteUsersPermissionsUser(id: ID!): UsersPermissionsUserEntityResponse!
deleteVendor(documentId: ID!): DeleteMutationResponse
"""Confirm an email users email address"""
emailConfirmation(confirmation: String!): UsersPermissionsLoginPayload
"""Request a reset password token"""
forgotPassword(email: String!): UsersPermissionsPasswordPayload
login(input: UsersPermissionsLoginInput!): UsersPermissionsLoginPayload!
"""Register a user"""
register(input: UsersPermissionsRegisterInput!): UsersPermissionsLoginPayload!
"""
Reset user password. Confirm with a code (resetToken from forgotPassword)
"""
resetPassword(code: String!, password: String!, passwordConfirmation: String!): UsersPermissionsLoginPayload
updateCategory(data: CategoryInput!, documentId: ID!, status: PublicationStatus = PUBLISHED): Category
updateHomePage(data: HomePageInput!, status: PublicationStatus = PUBLISHED): HomePage
updateReviewWorkflowsWorkflow(data: ReviewWorkflowsWorkflowInput!, documentId: ID!, status: PublicationStatus = PUBLISHED): ReviewWorkflowsWorkflow
updateReviewWorkflowsWorkflowStage(data: ReviewWorkflowsWorkflowStageInput!, documentId: ID!, status: PublicationStatus = PUBLISHED): ReviewWorkflowsWorkflowStage
updateSiteConfig(data: SiteConfigInput!, status: PublicationStatus = PUBLISHED): SiteConfig
updateUploadFile(id: ID!, info: FileInfoInput): UploadFile!
"""Update an existing role"""
updateUsersPermissionsRole(data: UsersPermissionsRoleInput!, id: ID!): UsersPermissionsUpdateRolePayload
"""Update an existing user"""
updateUsersPermissionsUser(data: UsersPermissionsUserInput!, id: ID!): UsersPermissionsUserEntityResponse!
updateVendor(data: VendorInput!, documentId: ID!, status: PublicationStatus = PUBLISHED): Vendor
}
type Pagination {
page: Int!
pageCount: Int!
pageSize: Int!
total: Int!
}
input PaginationArg {
limit: Int
page: Int
pageSize: Int
start: Int
}
enum PublicationStatus {
DRAFT
PUBLISHED
}
type Query {
categories(filters: CategoryFiltersInput, hasPublishedVersion: Boolean, pagination: PaginationArg = {}, sort: [String] = [], status: PublicationStatus = PUBLISHED): [Category]!
categories_connection(filters: CategoryFiltersInput, hasPublishedVersion: Boolean, pagination: PaginationArg = {}, sort: [String] = [], status: PublicationStatus = PUBLISHED): CategoryEntityResponseCollection
category(documentId: ID!, hasPublishedVersion: Boolean, status: PublicationStatus = PUBLISHED): Category
homePage(hasPublishedVersion: Boolean, status: PublicationStatus = PUBLISHED): HomePage
i18NLocale(documentId: ID!, hasPublishedVersion: Boolean, status: PublicationStatus = PUBLISHED): I18NLocale
i18NLocales(filters: I18NLocaleFiltersInput, hasPublishedVersion: Boolean, pagination: PaginationArg = {}, sort: [String] = [], status: PublicationStatus = PUBLISHED): [I18NLocale]!
i18NLocales_connection(filters: I18NLocaleFiltersInput, hasPublishedVersion: Boolean, pagination: PaginationArg = {}, sort: [String] = [], status: PublicationStatus = PUBLISHED): I18NLocaleEntityResponseCollection
me: UsersPermissionsMe
reviewWorkflowsWorkflow(documentId: ID!, hasPublishedVersion: Boolean, status: PublicationStatus = PUBLISHED): ReviewWorkflowsWorkflow
reviewWorkflowsWorkflowStage(documentId: ID!, hasPublishedVersion: Boolean, status: PublicationStatus = PUBLISHED): ReviewWorkflowsWorkflowStage
reviewWorkflowsWorkflowStages(filters: ReviewWorkflowsWorkflowStageFiltersInput, hasPublishedVersion: Boolean, pagination: PaginationArg = {}, sort: [String] = [], status: PublicationStatus = PUBLISHED): [ReviewWorkflowsWorkflowStage]!
reviewWorkflowsWorkflowStages_connection(filters: ReviewWorkflowsWorkflowStageFiltersInput, hasPublishedVersion: Boolean, pagination: PaginationArg = {}, sort: [String] = [], status: PublicationStatus = PUBLISHED): ReviewWorkflowsWorkflowStageEntityResponseCollection
reviewWorkflowsWorkflows(filters: ReviewWorkflowsWorkflowFiltersInput, hasPublishedVersion: Boolean, pagination: PaginationArg = {}, sort: [String] = [], status: PublicationStatus = PUBLISHED): [ReviewWorkflowsWorkflow]!
reviewWorkflowsWorkflows_connection(filters: ReviewWorkflowsWorkflowFiltersInput, hasPublishedVersion: Boolean, pagination: PaginationArg = {}, sort: [String] = [], status: PublicationStatus = PUBLISHED): ReviewWorkflowsWorkflowEntityResponseCollection
siteConfig(hasPublishedVersion: Boolean, status: PublicationStatus = PUBLISHED): SiteConfig
uploadFile(documentId: ID!, hasPublishedVersion: Boolean, status: PublicationStatus = PUBLISHED): UploadFile
uploadFiles(filters: UploadFileFiltersInput, hasPublishedVersion: Boolean, pagination: PaginationArg = {}, sort: [String] = [], status: PublicationStatus = PUBLISHED): [UploadFile]!
uploadFiles_connection(filters: UploadFileFiltersInput, hasPublishedVersion: Boolean, pagination: PaginationArg = {}, sort: [String] = [], status: PublicationStatus = PUBLISHED): UploadFileEntityResponseCollection
usersPermissionsRole(documentId: ID!, hasPublishedVersion: Boolean, status: PublicationStatus = PUBLISHED): UsersPermissionsRole
usersPermissionsRoles(filters: UsersPermissionsRoleFiltersInput, hasPublishedVersion: Boolean, pagination: PaginationArg = {}, sort: [String] = [], status: PublicationStatus = PUBLISHED): [UsersPermissionsRole]!
usersPermissionsRoles_connection(filters: UsersPermissionsRoleFiltersInput, hasPublishedVersion: Boolean, pagination: PaginationArg = {}, sort: [String] = [], status: PublicationStatus = PUBLISHED): UsersPermissionsRoleEntityResponseCollection
usersPermissionsUser(documentId: ID!, hasPublishedVersion: Boolean, status: PublicationStatus = PUBLISHED): UsersPermissionsUser
usersPermissionsUsers(filters: UsersPermissionsUserFiltersInput, hasPublishedVersion: Boolean, pagination: PaginationArg = {}, sort: [String] = [], status: PublicationStatus = PUBLISHED): [UsersPermissionsUser]!
usersPermissionsUsers_connection(filters: UsersPermissionsUserFiltersInput, hasPublishedVersion: Boolean, pagination: PaginationArg = {}, sort: [String] = [], status: PublicationStatus = PUBLISHED): UsersPermissionsUserEntityResponseCollection
vendor(documentId: ID!, hasPublishedVersion: Boolean, status: PublicationStatus = PUBLISHED): Vendor
vendors(filters: VendorFiltersInput, hasPublishedVersion: Boolean, pagination: PaginationArg = {}, sort: [String] = [], status: PublicationStatus = PUBLISHED): [Vendor]!
vendors_connection(filters: VendorFiltersInput, hasPublishedVersion: Boolean, pagination: PaginationArg = {}, sort: [String] = [], status: PublicationStatus = PUBLISHED): VendorEntityResponseCollection
}
type ReviewWorkflowsWorkflow {
contentTypes: JSON!
createdAt: DateTime
documentId: ID!
name: String!
publishedAt: DateTime
stageRequiredToPublish: ReviewWorkflowsWorkflowStage
stages(filters: ReviewWorkflowsWorkflowStageFiltersInput, pagination: PaginationArg = {}, sort: [String] = []): [ReviewWorkflowsWorkflowStage]!
stages_connection(filters: ReviewWorkflowsWorkflowStageFiltersInput, pagination: PaginationArg = {}, sort: [String] = []): ReviewWorkflowsWorkflowStageRelationResponseCollection
updatedAt: DateTime
}
type ReviewWorkflowsWorkflowEntityResponseCollection {
nodes: [ReviewWorkflowsWorkflow!]!
pageInfo: Pagination!
}
input ReviewWorkflowsWorkflowFiltersInput {
and: [ReviewWorkflowsWorkflowFiltersInput]
contentTypes: JSONFilterInput
createdAt: DateTimeFilterInput
documentId: IDFilterInput
name: StringFilterInput
not: ReviewWorkflowsWorkflowFiltersInput
or: [ReviewWorkflowsWorkflowFiltersInput]
publishedAt: DateTimeFilterInput
stageRequiredToPublish: ReviewWorkflowsWorkflowStageFiltersInput
stages: ReviewWorkflowsWorkflowStageFiltersInput
updatedAt: DateTimeFilterInput
}
input ReviewWorkflowsWorkflowInput {
contentTypes: JSON
name: String
publishedAt: DateTime
stageRequiredToPublish: ID
stages: [ID]
}
type ReviewWorkflowsWorkflowStage {
color: String
createdAt: DateTime
documentId: ID!
name: String
publishedAt: DateTime
updatedAt: DateTime
workflow: ReviewWorkflowsWorkflow
}
type ReviewWorkflowsWorkflowStageEntityResponseCollection {
nodes: [ReviewWorkflowsWorkflowStage!]!
pageInfo: Pagination!
}
input ReviewWorkflowsWorkflowStageFiltersInput {
and: [ReviewWorkflowsWorkflowStageFiltersInput]
color: StringFilterInput
createdAt: DateTimeFilterInput
documentId: IDFilterInput
name: StringFilterInput
not: ReviewWorkflowsWorkflowStageFiltersInput
or: [ReviewWorkflowsWorkflowStageFiltersInput]
publishedAt: DateTimeFilterInput
updatedAt: DateTimeFilterInput
workflow: ReviewWorkflowsWorkflowFiltersInput
}
input ReviewWorkflowsWorkflowStageInput {
color: String
name: String
publishedAt: DateTime
workflow: ID
}
type ReviewWorkflowsWorkflowStageRelationResponseCollection {
nodes: [ReviewWorkflowsWorkflowStage!]!
}
type SiteConfig {
createdAt: DateTime
description: String
documentId: ID!
publishedAt: DateTime
title: String!
updatedAt: DateTime
}
input SiteConfigInput {
description: String
publishedAt: DateTime
title: String
}
input StringFilterInput {
and: [String]
between: [String]
contains: String
containsi: String
endsWith: String
eq: String
eqi: String
gt: String
gte: String
in: [String]
lt: String
lte: String
ne: String
nei: String
not: StringFilterInput
notContains: String
notContainsi: String
notIn: [String]
notNull: Boolean
null: Boolean
or: [String]
startsWith: String
}
type UploadFile {
alternativeText: String
caption: String
createdAt: DateTime
documentId: ID!
ext: String
focalPoint: JSON
formats: JSON
hash: String!
height: Int
mime: String!
name: String!
previewUrl: String
provider: String!
provider_metadata: JSON
publishedAt: DateTime
related: [GenericMorph]
size: Float!
updatedAt: DateTime
url: String!
width: Int
}
type UploadFileEntityResponseCollection {
nodes: [UploadFile!]!
pageInfo: Pagination!
}
input UploadFileFiltersInput {
alternativeText: StringFilterInput
and: [UploadFileFiltersInput]
caption: StringFilterInput
createdAt: DateTimeFilterInput
documentId: IDFilterInput
ext: StringFilterInput
focalPoint: JSONFilterInput
formats: JSONFilterInput
hash: StringFilterInput
height: IntFilterInput
mime: StringFilterInput
name: StringFilterInput
not: UploadFileFiltersInput
or: [UploadFileFiltersInput]
previewUrl: StringFilterInput
provider: StringFilterInput
provider_metadata: JSONFilterInput
publishedAt: DateTimeFilterInput
size: FloatFilterInput
updatedAt: DateTimeFilterInput
url: StringFilterInput
width: IntFilterInput
}
type UsersPermissionsCreateRolePayload {
ok: Boolean!
}
type UsersPermissionsDeleteRolePayload {
ok: Boolean!
}
input UsersPermissionsLoginInput {
identifier: String!
password: String!
provider: String! = "local"
}
type UsersPermissionsLoginPayload {
jwt: String
user: UsersPermissionsMe!
}
type UsersPermissionsMe {
blocked: Boolean
confirmed: Boolean
documentId: ID!
email: String
id: ID!
role: UsersPermissionsMeRole
username: String!
}
type UsersPermissionsMeRole {
description: String
id: ID!
name: String!
type: String
}
type UsersPermissionsPasswordPayload {
ok: Boolean!
}
type UsersPermissionsPermission {
action: String!
createdAt: DateTime
documentId: ID!
publishedAt: DateTime
role: UsersPermissionsRole
updatedAt: DateTime
}
input UsersPermissionsPermissionFiltersInput {
action: StringFilterInput
and: [UsersPermissionsPermissionFiltersInput]
createdAt: DateTimeFilterInput
documentId: IDFilterInput
not: UsersPermissionsPermissionFiltersInput
or: [UsersPermissionsPermissionFiltersInput]
publishedAt: DateTimeFilterInput
role: UsersPermissionsRoleFiltersInput
updatedAt: DateTimeFilterInput
}
type UsersPermissionsPermissionRelationResponseCollection {
nodes: [UsersPermissionsPermission!]!
}
input UsersPermissionsRegisterInput {
email: String!
password: String!
username: String!
}
type UsersPermissionsRole {
createdAt: DateTime
description: String
documentId: ID!
name: String!
permissions(filters: UsersPermissionsPermissionFiltersInput, pagination: PaginationArg = {}, sort: [String] = []): [UsersPermissionsPermission]!
permissions_connection(filters: UsersPermissionsPermissionFiltersInput, pagination: PaginationArg = {}, sort: [String] = []): UsersPermissionsPermissionRelationResponseCollection
publishedAt: DateTime
type: String
updatedAt: DateTime
users(filters: UsersPermissionsUserFiltersInput, pagination: PaginationArg = {}, sort: [String] = []): [UsersPermissionsUser]!
users_connection(filters: UsersPermissionsUserFiltersInput, pagination: PaginationArg = {}, sort: [String] = []): UsersPermissionsUserRelationResponseCollection
}
type UsersPermissionsRoleEntityResponseCollection {
nodes: [UsersPermissionsRole!]!
pageInfo: Pagination!
}
input UsersPermissionsRoleFiltersInput {
and: [UsersPermissionsRoleFiltersInput]
createdAt: DateTimeFilterInput
description: StringFilterInput
documentId: IDFilterInput
name: StringFilterInput
not: UsersPermissionsRoleFiltersInput
or: [UsersPermissionsRoleFiltersInput]
permissions: UsersPermissionsPermissionFiltersInput
publishedAt: DateTimeFilterInput
type: StringFilterInput
updatedAt: DateTimeFilterInput
users: UsersPermissionsUserFiltersInput
}
input UsersPermissionsRoleInput {
description: String
name: String
permissions: [ID]
publishedAt: DateTime
type: String
users: [ID]
}
type UsersPermissionsUpdateRolePayload {
ok: Boolean!
}
type UsersPermissionsUser {
blocked: Boolean
confirmed: Boolean
createdAt: DateTime
documentId: ID!
email: String!
provider: String
publishedAt: DateTime
role: UsersPermissionsRole
updatedAt: DateTime
username: String!
}
type UsersPermissionsUserEntityResponse {
data: UsersPermissionsUser
}
type UsersPermissionsUserEntityResponseCollection {
nodes: [UsersPermissionsUser!]!
pageInfo: Pagination!
}
input UsersPermissionsUserFiltersInput {
and: [UsersPermissionsUserFiltersInput]
blocked: BooleanFilterInput
confirmed: BooleanFilterInput
createdAt: DateTimeFilterInput
documentId: IDFilterInput
email: StringFilterInput
not: UsersPermissionsUserFiltersInput
or: [UsersPermissionsUserFiltersInput]
provider: StringFilterInput
publishedAt: DateTimeFilterInput
role: UsersPermissionsRoleFiltersInput
updatedAt: DateTimeFilterInput
username: StringFilterInput
}
input UsersPermissionsUserInput {
blocked: Boolean
confirmed: Boolean
email: String
password: String
provider: String
publishedAt: DateTime
role: ID
username: String
}
type UsersPermissionsUserRelationResponseCollection {
nodes: [UsersPermissionsUser!]!
}
type Vendor {
categories(filters: CategoryFiltersInput, pagination: PaginationArg = {}, sort: [String] = []): [Category]!
categories_connection(filters: CategoryFiltersInput, pagination: PaginationArg = {}, sort: [String] = []): CategoryRelationResponseCollection
createdAt: DateTime
description: String
documentId: ID!
products(filters: ComponentSharedStringFiltersInput, pagination: PaginationArg = {}, sort: [String] = []): [ComponentSharedString]
publishedAt: DateTime
slug: String!
title: String
updatedAt: DateTime
}
type VendorEntityResponseCollection {
nodes: [Vendor!]!
pageInfo: Pagination!
}
input VendorFiltersInput {
and: [VendorFiltersInput]
categories: CategoryFiltersInput
createdAt: DateTimeFilterInput
description: StringFilterInput
documentId: IDFilterInput
not: VendorFiltersInput
or: [VendorFiltersInput]
products: ComponentSharedStringFiltersInput
publishedAt: DateTimeFilterInput
slug: StringFilterInput
title: StringFilterInput
updatedAt: DateTimeFilterInput
}
input VendorInput {
categories: [ID]
description: String
products: [ComponentSharedStringInput]
publishedAt: DateTime
slug: String
title: String
}
type VendorRelationResponseCollection {
nodes: [Vendor!]!
}

View File

@@ -0,0 +1,119 @@
#!/usr/bin/env node
/**
* Usage: npm run endpoint:generate -- [slug]
* Example: npm run endpoint:generate -- home-page
* Expects src/.../slug.gql.ts with export const name = gql`...`
* Runs graphql-codegen, then writes slug.gql.generated.ts beside it.
*/
import { execSync } from "node:child_process";
import fs from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const root = path.resolve(__dirname, "..");
const slug = process.argv[2];
if (!slug || slug.startsWith("-")) {
console.error("Usage: npm run endpoint:generate -- <slug>");
console.error('Example: npm run endpoint:generate -- home-page');
process.exit(1);
}
function findGqlFile() {
const exact = path.join(root, "src", `${slug}.gql.ts`);
if (fs.existsSync(exact)) return path.relative(root, exact);
const stack = [path.join(root, "src")];
while (stack.length) {
const dir = stack.pop();
if (!fs.existsSync(dir)) continue;
for (const name of fs.readdirSync(dir, { withFileTypes: true })) {
const p = path.join(dir, name.name);
if (name.isDirectory()) stack.push(p);
else if (name.name === `${slug}.gql.ts`) return path.relative(root, p);
}
}
return null;
}
const gqlPath = findGqlFile();
if (!gqlPath) {
console.error(`No file named "${slug}.gql.ts" found under src/`);
process.exit(1);
}
const absGql = path.join(root, gqlPath);
const source = fs.readFileSync(absGql, "utf8");
const exportMatch = source.match(/export\s+const\s+(\w+)\s*=\s*gql\s*`/);
if (!exportMatch) {
console.error(`${gqlPath}: expected: export const SomeName = gql\`...\``);
process.exit(1);
}
const documentExport = exportMatch[1];
const opMatch = source.match(/\b(query|mutation|subscription)\s+(\w+)\s*[\({]/);
if (!opMatch) {
console.error(`${gqlPath}: could not find operation name (query|mutation|subscription Name)`);
process.exit(1);
}
const operationKind = opMatch[1];
const operationName = opMatch[2];
const resultType =
operationKind === "mutation"
? `${operationName}Mutation`
: operationKind === "subscription"
? `${operationName}Subscription`
: `${operationName}Query`;
const variablesType = `${resultType}Variables`;
const endpointName =
operationKind === "mutation"
? operationName[0].toLowerCase() + operationName.slice(1) + "Mutation"
: operationKind === "subscription"
? operationName[0].toLowerCase() + operationName.slice(1) + "Subscription"
: operationName[0].toLowerCase() + operationName.slice(1) + "Query";
const outPath = absGql.replace(/\.gql\.ts$/, ".gql.generated.ts");
const outRel = path.relative(root, outPath);
const gqlDir = path.dirname(absGql);
const executeRel = path
.relative(gqlDir, path.join(root, "src", "graphql", "execute.ts"))
.replace(/\\/g, "/")
.replace(/\.ts$/, "");
const typesRel = path
.relative(gqlDir, path.join(root, "src", "graphql", "graphql.ts"))
.replace(/\\/g, "/")
.replace(/\.ts$/, "");
const gqlBase = path.basename(gqlPath);
const gqlImportStem = gqlBase.replace(/\.ts$/, "");
console.log("Running graphql-codegen…");
execSync("npx graphql-codegen", { cwd: root, stdio: "inherit" });
function quoteImport(rel) {
if (rel.startsWith(".")) return rel;
return `./${rel}`;
}
const body = `/* eslint-disable */
/* Generated by scripts/endpoint-generate.mjs — do not edit by hand */
import type { ExecutionResult } from "graphql";
import { execute } from "${quoteImport(executeRel)}";
import type { ${resultType}, ${variablesType} } from "${quoteImport(typesRel)}";
import { ${documentExport} } from "./${gqlImportStem}";
export const ${endpointName} = {
async execute(variables?: ${variablesType}): Promise<ExecutionResult<${resultType}>> {
return execute<${resultType}, ${variablesType}>(${documentExport}, variables);
},
};
`;
fs.writeFileSync(outPath, body, "utf8");
console.log(`Wrote ${outRel}`);

View File

@@ -0,0 +1,13 @@
/* eslint-disable */
/* Generated by scripts/endpoint-generate.mjs — do not edit by hand */
import type { ExecutionResult } from "graphql";
import { execute } from "../graphql/execute";
import type { CategoriesListQuery, CategoriesListQueryVariables } from "../graphql/graphql";
import { categoriesListDocument } from "./categories.gql";
export const categoriesListQuery = {
async execute(variables?: CategoriesListQueryVariables): Promise<ExecutionResult<CategoriesListQuery>> {
return execute<CategoriesListQuery, CategoriesListQueryVariables>(categoriesListDocument, variables);
},
};

View File

@@ -0,0 +1,14 @@
import gql from "graphql-tag";
export const categoriesListDocument = gql`
query CategoriesList {
categories {
documentId
slug
title
sortOrder
group
badge
}
}
`;

View File

@@ -0,0 +1,13 @@
/* eslint-disable */
/* Generated by scripts/endpoint-generate.mjs — do not edit by hand */
import type { ExecutionResult } from "graphql";
import { execute } from "../graphql/execute";
import type { HomePageQuery, HomePageQueryVariables } from "../graphql/graphql";
import { homePageDocument } from "./home-page.gql";
export const homePageQuery = {
async execute(variables?: HomePageQueryVariables): Promise<ExecutionResult<HomePageQuery>> {
return execute<HomePageQuery, HomePageQueryVariables>(homePageDocument, variables);
},
};

View File

@@ -0,0 +1,38 @@
import gql from "graphql-tag";
export const homePageDocument = gql`
query HomePage {
homePage {
sections {
... on ComponentSectionsHomeHero {
title
description
badge
stats {
id
key
value
}
}
... on ComponentSectionsServices {
title
description
cards {
id
title
description
color {
value
}
icon {
value
}
category {
slug
}
}
}
}
}
}
`;

53
src/graphql/execute.ts Normal file
View File

@@ -0,0 +1,53 @@
import type { DocumentNode, ExecutionResult } from "graphql";
import { print } from "graphql";
import type { TypedDocumentString } from "./graphql";
const defaultUrl = "http://localhost:1337/graphql";
function getEndpoint(): string {
if (typeof import.meta !== "undefined" && import.meta.env?.PUBLIC_GRAPHQL_URL) {
return import.meta.env.PUBLIC_GRAPHQL_URL;
}
if (typeof process !== "undefined" && process.env.PUBLIC_GRAPHQL_URL) {
return process.env.PUBLIC_GRAPHQL_URL;
}
return defaultUrl;
}
function toQueryString(query: TypedDocumentString<unknown, unknown> | DocumentNode): string {
if (typeof query === "object" && query !== null && "kind" in query && query.kind === "Document") {
return print(query as DocumentNode);
}
return String(query);
}
/**
* GraphQL over HTTP. Accepts codegen {@link TypedDocumentString} or a `graphql-tag` {@link DocumentNode}.
*/
export async function execute<TResult, TVariables>(
query: TypedDocumentString<TResult, TVariables> | DocumentNode,
variables?: TVariables
): Promise<ExecutionResult<TResult>> {
const payload: { query: string; variables?: TVariables } = {
query: toQueryString(query as TypedDocumentString<unknown, unknown> | DocumentNode),
};
if (variables !== undefined) {
payload.variables = variables;
}
const response = await fetch(getEndpoint(), {
method: "POST",
headers: {
"Content-Type": "application/json",
Accept: "application/graphql-response+json, application/json",
},
body: JSON.stringify(payload),
});
if (!response.ok) {
throw new Error(`GraphQL HTTP ${response.status}: ${response.statusText}`);
}
return response.json() as Promise<ExecutionResult<TResult>>;
}

1494
src/graphql/graphql.ts Normal file

File diff suppressed because it is too large Load Diff

1
src/graphql/index.ts Normal file
View File

@@ -0,0 +1 @@
export * from "./graphql";