feat(portal-shell): extract gql documents to operations layer
M1 Task 3: 从 31 widget 抽取 51 个 gql 文档到 7 个 operations 文件 - universal(7) + sidebar(3) + topbar(3) + teacher(6) + student(8) + parent(4) + admin(20) = 51 DOC - operations/index.ts barrel 统一出口 - codegen.yml 启用 documents + skipDocumentsValidation(services 子图字段待补齐) - 生成 types.ts (28KB) + operations.ts (10KB)
This commit is contained in:
275
apps/portal-shell/src/lib/api/operations/admin.graphql.ts
Normal file
275
apps/portal-shell/src/lib/api/operations/admin.graphql.ts
Normal file
@@ -0,0 +1,275 @@
|
||||
// Admin domain GraphQL documents
|
||||
// Extracted from widgets/admin/*/index.tsx
|
||||
// Related: spec section 2.2
|
||||
import { gql } from "@apollo/client";
|
||||
|
||||
// From widgets/admin/user-management
|
||||
export const GET_USERS_DOC = gql`
|
||||
query GetUsers($role: String, $limit: Int, $offset: Int) {
|
||||
users(role: $role, limit: $limit, offset: $offset) {
|
||||
items {
|
||||
id
|
||||
name
|
||||
email
|
||||
role
|
||||
status
|
||||
createdAt
|
||||
}
|
||||
total
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
// From widgets/admin/user-management
|
||||
export const UPDATE_USER_STATUS_DOC = gql`
|
||||
mutation UpdateUserStatus($id: ID!, $status: String!) {
|
||||
updateUserStatus(id: $id, status: $status) {
|
||||
id
|
||||
status
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
// From widgets/admin/user-management
|
||||
export const UPDATE_USER_ROLE_DOC = gql`
|
||||
mutation UpdateUserRole($id: ID!, $role: String!) {
|
||||
updateUserRole(id: $id, role: $role) {
|
||||
id
|
||||
role
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
// From widgets/admin/rbac-manager
|
||||
export const GET_ROLES_DOC = gql`
|
||||
query GetRoles {
|
||||
roles {
|
||||
id
|
||||
name
|
||||
permissions {
|
||||
id
|
||||
name
|
||||
resource
|
||||
action
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
// From widgets/admin/rbac-manager
|
||||
export const GET_PERMISSIONS_DOC = gql`
|
||||
query GetPermissions {
|
||||
permissions {
|
||||
id
|
||||
name
|
||||
resource
|
||||
action
|
||||
description
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
// From widgets/admin/rbac-manager
|
||||
export const UPDATE_ROLE_PERMISSIONS_DOC = gql`
|
||||
mutation UpdateRolePermissions($roleId: ID!, $permissionIds: [ID]!) {
|
||||
updateRolePermissions(roleId: $roleId, permissionIds: $permissionIds) {
|
||||
id
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
// From widgets/admin/audit-logs
|
||||
export const GET_AUDIT_LOGS_DOC = gql`
|
||||
query GetAuditLogs($filter: AuditLogFilter, $limit: Int, $offset: Int) {
|
||||
auditLogs(filter: $filter, limit: $limit, offset: $offset) {
|
||||
items {
|
||||
id
|
||||
userId
|
||||
userName
|
||||
action
|
||||
resource
|
||||
resourceId
|
||||
ip
|
||||
timestamp
|
||||
details
|
||||
}
|
||||
total
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
// From widgets/admin/invitation-codes
|
||||
export const GET_INVITATION_CODES_DOC = gql`
|
||||
query GetInvitationCodes($status: String) {
|
||||
invitationCodes(status: $status) {
|
||||
id
|
||||
code
|
||||
role
|
||||
status
|
||||
usedCount
|
||||
maxUses
|
||||
expiresAt
|
||||
createdAt
|
||||
createdBy
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
// From widgets/admin/invitation-codes
|
||||
export const CREATE_INVITATION_CODE_DOC = gql`
|
||||
mutation CreateInvitationCode($input: CreateInvitationCodeInput!) {
|
||||
createInvitationCode(input: $input) {
|
||||
id
|
||||
code
|
||||
role
|
||||
maxUses
|
||||
expiresAt
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
// From widgets/admin/invitation-codes
|
||||
export const REVOKE_INVITATION_CODE_DOC = gql`
|
||||
mutation RevokeInvitationCode($id: ID!) {
|
||||
revokeInvitationCode(id: $id) {
|
||||
id
|
||||
status
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
// From widgets/admin/school-settings
|
||||
export const GET_SCHOOL_DOC = gql`
|
||||
query GetSchool {
|
||||
school {
|
||||
id
|
||||
name
|
||||
address
|
||||
phone
|
||||
email
|
||||
currentAcademicYear
|
||||
currentTerm
|
||||
semesterStart
|
||||
semesterEnd
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
// From widgets/admin/school-settings
|
||||
export const UPDATE_SCHOOL_DOC = gql`
|
||||
mutation UpdateSchool($input: SchoolInput!) {
|
||||
updateSchool(input: $input) {
|
||||
id
|
||||
name
|
||||
address
|
||||
phone
|
||||
email
|
||||
currentAcademicYear
|
||||
currentTerm
|
||||
semesterStart
|
||||
semesterEnd
|
||||
updatedAt
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
// From widgets/admin/plugin-manager
|
||||
export const GET_PLUGIN_REGISTRY_DOC = gql`
|
||||
query GetPluginRegistry {
|
||||
pluginRegistry {
|
||||
pluginId
|
||||
category
|
||||
version
|
||||
displayName
|
||||
description
|
||||
requiredRoles
|
||||
isBuiltin
|
||||
isActive
|
||||
defaultSlot
|
||||
defaultSize
|
||||
defaultProps
|
||||
propsSchema
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
// From widgets/admin/plugin-manager
|
||||
export const GET_ROLE_PLUGIN_MAPPING_DOC = gql`
|
||||
query GetRolePluginMapping($role: String) {
|
||||
rolePluginMapping(role: $role) {
|
||||
role
|
||||
pluginId
|
||||
slot
|
||||
sortOrder
|
||||
isEnabled
|
||||
widgetProps
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
// From widgets/admin/plugin-manager
|
||||
export const GET_LAYOUT_TEMPLATES_DOC = gql`
|
||||
query GetLayoutTemplates {
|
||||
layoutTemplates {
|
||||
layoutId
|
||||
displayName
|
||||
description
|
||||
availableSlots
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
// From widgets/admin/plugin-manager
|
||||
export const GET_ROLE_LAYOUT_DEFAULT_DOC = gql`
|
||||
query GetRoleLayoutDefault($role: String) {
|
||||
roleLayoutDefault(role: $role) {
|
||||
role
|
||||
layoutId
|
||||
slotOverrides
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
// From widgets/admin/plugin-manager
|
||||
export const UPDATE_PLUGIN_REGISTRY_DOC = gql`
|
||||
mutation UpdatePluginRegistry($pluginId: ID!, $input: PluginRegistryInput!) {
|
||||
updatePluginRegistry(pluginId: $pluginId, input: $input) {
|
||||
pluginId
|
||||
isActive
|
||||
defaultProps
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
// From widgets/admin/plugin-manager
|
||||
export const UPDATE_ROLE_PLUGIN_MAPPING_DOC = gql`
|
||||
mutation UpdateRoleMapping(
|
||||
$role: String!
|
||||
$mappings: [RolePluginMappingInput!]!
|
||||
) {
|
||||
updateRolePluginMapping(role: $role, mappings: $mappings) {
|
||||
role
|
||||
pluginId
|
||||
isEnabled
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
// From widgets/admin/plugin-manager
|
||||
export const UPDATE_ROLE_LAYOUT_DEFAULT_DOC = gql`
|
||||
mutation UpdateRoleLayoutDefault($role: String!, $layoutId: String!) {
|
||||
updateRoleLayoutDefault(role: $role, layoutId: $layoutId) {
|
||||
role
|
||||
layoutId
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
// From widgets/admin/plugin-manager
|
||||
export const RESET_USER_LAYOUT_OVERRIDE_DOC = gql`
|
||||
mutation ResetUserLayoutOverride($userId: ID!) {
|
||||
resetUserLayoutOverride(userId: $userId) {
|
||||
userId
|
||||
}
|
||||
}
|
||||
`;
|
||||
9
apps/portal-shell/src/lib/api/operations/index.ts
Normal file
9
apps/portal-shell/src/lib/api/operations/index.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
// Operations unified export
|
||||
// All GraphQL documents centralized for lib/api/<domain>.ts consumption.
|
||||
export * from "./universal.graphql";
|
||||
export * from "./sidebar.graphql";
|
||||
export * from "./topbar.graphql";
|
||||
export * from "./teacher.graphql";
|
||||
export * from "./student.graphql";
|
||||
export * from "./parent.graphql";
|
||||
export * from "./admin.graphql";
|
||||
67
apps/portal-shell/src/lib/api/operations/parent.graphql.ts
Normal file
67
apps/portal-shell/src/lib/api/operations/parent.graphql.ts
Normal file
@@ -0,0 +1,67 @@
|
||||
// Parent domain GraphQL documents
|
||||
// Extracted from widgets/parent/*/index.tsx
|
||||
// Related: spec section 2.2
|
||||
import { gql } from "@apollo/client";
|
||||
|
||||
// From widgets/parent/child-overview
|
||||
// Note: renamed from GET_MY_CHILDREN to GET_MY_CHILDREN_OVERVIEW_DOC to avoid
|
||||
// collision with sidebar's GET_MY_CHILDREN_DOC (different field selection).
|
||||
export const GET_MY_CHILDREN_OVERVIEW_DOC = gql`
|
||||
query GetMyChildrenOverview {
|
||||
myChildren {
|
||||
id
|
||||
name
|
||||
grade
|
||||
className
|
||||
avatar
|
||||
recentGrades {
|
||||
subject
|
||||
score
|
||||
}
|
||||
attendance {
|
||||
present
|
||||
total
|
||||
}
|
||||
homeworkCompletion {
|
||||
completed
|
||||
total
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
// From widgets/parent/leave-approval
|
||||
export const GET_LEAVE_REQUESTS_DOC = gql`
|
||||
query GetLeaveRequests($childId: ID, $status: String) {
|
||||
leaveRequests(childId: $childId, status: $status) {
|
||||
id
|
||||
childName
|
||||
type
|
||||
startDate
|
||||
endDate
|
||||
reason
|
||||
status
|
||||
createdAt
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
// From widgets/parent/leave-approval
|
||||
export const APPROVE_LEAVE_DOC = gql`
|
||||
mutation ApproveLeave($id: ID!) {
|
||||
approveLeave(id: $id) {
|
||||
id
|
||||
status
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
// From widgets/parent/leave-approval
|
||||
export const REJECT_LEAVE_DOC = gql`
|
||||
mutation RejectLeave($id: ID!, $reason: String) {
|
||||
rejectLeave(id: $id, reason: $reason) {
|
||||
id
|
||||
status
|
||||
}
|
||||
}
|
||||
`;
|
||||
41
apps/portal-shell/src/lib/api/operations/sidebar.graphql.ts
Normal file
41
apps/portal-shell/src/lib/api/operations/sidebar.graphql.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
// Sidebar domain GraphQL documents
|
||||
// Extracted from widgets/sidebar/*/index.tsx
|
||||
// Related: spec section 2.2
|
||||
import { gql } from "@apollo/client";
|
||||
|
||||
// From widgets/sidebar/class-selector
|
||||
export const GET_MY_CLASSES_DOC = gql`
|
||||
query GetMyClasses {
|
||||
myClasses {
|
||||
id
|
||||
name
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
// From widgets/sidebar/child-selector
|
||||
export const GET_MY_CHILDREN_DOC = gql`
|
||||
query GetMyChildren {
|
||||
myChildren {
|
||||
id
|
||||
name
|
||||
grade
|
||||
className
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
// From widgets/sidebar/term-switcher
|
||||
export const GET_TERMS_DOC = gql`
|
||||
query GetTerms {
|
||||
terms {
|
||||
id
|
||||
name
|
||||
startDate
|
||||
endDate
|
||||
isActive
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
// Note: widgets/sidebar/quick-actions has no GraphQL (pure UI navigation).
|
||||
94
apps/portal-shell/src/lib/api/operations/student.graphql.ts
Normal file
94
apps/portal-shell/src/lib/api/operations/student.graphql.ts
Normal file
@@ -0,0 +1,94 @@
|
||||
// Student domain GraphQL documents
|
||||
// Extracted from widgets/student/*/index.tsx
|
||||
// Related: spec section 2.2
|
||||
import { gql } from "@apollo/client";
|
||||
|
||||
// From widgets/student/error-book
|
||||
export const GET_ERROR_BOOK_DOC = gql`
|
||||
query MyErrorBook($subjectId: ID, $limit: Int) {
|
||||
myErrorBook(subjectId: $subjectId, limit: $limit) {
|
||||
id
|
||||
question
|
||||
myAnswer
|
||||
correctAnswer
|
||||
errorCount
|
||||
lastErrorAt
|
||||
subject
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
// From widgets/student/error-book
|
||||
export const MARK_ERROR_MASTERED_DOC = gql`
|
||||
mutation MarkErrorMastered($id: ID!) {
|
||||
markErrorMastered(id: $id)
|
||||
}
|
||||
`;
|
||||
|
||||
// From widgets/student/learning-path
|
||||
export const GET_LEARNING_PATH_DOC = gql`
|
||||
query MyLearningPath($subjectId: ID!) {
|
||||
myLearningPath(subjectId: $subjectId) {
|
||||
nodes {
|
||||
id
|
||||
title
|
||||
type
|
||||
status
|
||||
dependencies
|
||||
}
|
||||
progress
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
// From widgets/student/elective-selector
|
||||
export const GET_ELECTIVE_COURSES_DOC = gql`
|
||||
query ElectiveCourses($termId: ID!) {
|
||||
electiveCourses(termId: $termId) {
|
||||
id
|
||||
name
|
||||
teacher
|
||||
capacity
|
||||
enrolled
|
||||
schedule
|
||||
credits
|
||||
category
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
// From widgets/student/elective-selector
|
||||
export const ENROLL_COURSE_DOC = gql`
|
||||
mutation EnrollCourse($courseId: ID!) {
|
||||
enrollCourse(courseId: $courseId)
|
||||
}
|
||||
`;
|
||||
|
||||
// From widgets/student/elective-selector
|
||||
export const DROP_COURSE_DOC = gql`
|
||||
mutation DropCourse($courseId: ID!) {
|
||||
dropCourse(courseId: $courseId)
|
||||
}
|
||||
`;
|
||||
|
||||
// From widgets/student/ai-tutor
|
||||
export const GET_AI_SESSIONS_DOC = gql`
|
||||
query AiTutorSessions($limit: Int) {
|
||||
aiTutorSessions(limit: $limit) {
|
||||
id
|
||||
title
|
||||
lastMessage
|
||||
updatedAt
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
// From widgets/student/ai-tutor
|
||||
export const SEND_MESSAGE_DOC = gql`
|
||||
mutation SendAiTutorMessage($sessionId: ID, $message: String!) {
|
||||
sendAiTutorMessage(sessionId: $sessionId, message: $message) {
|
||||
sessionId
|
||||
reply
|
||||
}
|
||||
}
|
||||
`;
|
||||
81
apps/portal-shell/src/lib/api/operations/teacher.graphql.ts
Normal file
81
apps/portal-shell/src/lib/api/operations/teacher.graphql.ts
Normal file
@@ -0,0 +1,81 @@
|
||||
// Teacher domain GraphQL documents
|
||||
// Extracted from widgets/teacher/*/index.tsx
|
||||
// Related: spec section 2.2
|
||||
import { gql } from "@apollo/client";
|
||||
|
||||
// From widgets/teacher/lesson-plan-editor
|
||||
export const GET_LESSON_PLANS_DOC = gql`
|
||||
query GetLessonPlans($classId: ID!, $unitId: ID) {
|
||||
lessonPlans(classId: $classId, unitId: $unitId) {
|
||||
id
|
||||
title
|
||||
objectives
|
||||
content
|
||||
resources
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
// From widgets/teacher/lesson-plan-editor
|
||||
export const SAVE_LESSON_PLAN_DOC = gql`
|
||||
mutation SaveLessonPlan($input: LessonPlanInput!) {
|
||||
saveLessonPlan(input: $input) {
|
||||
id
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
// From widgets/teacher/question-bank
|
||||
export const GET_QUESTIONS_DOC = gql`
|
||||
query GetQuestions($bankId: ID!, $type: String, $limit: Int) {
|
||||
questions(bankId: $bankId, type: $type, limit: $limit) {
|
||||
id
|
||||
type
|
||||
difficulty
|
||||
content
|
||||
options
|
||||
answer
|
||||
tags
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
// From widgets/teacher/textbook-manager
|
||||
export const GET_TEXTBOOKS_DOC = gql`
|
||||
query GetTextbooks($subjectId: ID, $grade: String) {
|
||||
textbooks(subjectId: $subjectId, grade: $grade) {
|
||||
id
|
||||
title
|
||||
author
|
||||
publisher
|
||||
isbn
|
||||
chapters {
|
||||
id
|
||||
title
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
// From widgets/teacher/scheduling-rules
|
||||
export const GET_SCHEDULING_RULES_DOC = gql`
|
||||
query GetSchedulingRules($classId: ID!) {
|
||||
schedulingRules(classId: $classId) {
|
||||
id
|
||||
dayOfWeek
|
||||
periods
|
||||
subject
|
||||
teacherId
|
||||
room
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
// From widgets/teacher/scheduling-rules
|
||||
export const UPDATE_SCHEDULING_RULE_DOC = gql`
|
||||
mutation UpdateSchedulingRule($id: ID!, $input: SchedulingRuleInput!) {
|
||||
updateSchedulingRule(id: $id, input: $input) {
|
||||
id
|
||||
}
|
||||
}
|
||||
`;
|
||||
40
apps/portal-shell/src/lib/api/operations/topbar.graphql.ts
Normal file
40
apps/portal-shell/src/lib/api/operations/topbar.graphql.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
// Topbar domain GraphQL documents
|
||||
// Extracted from widgets/topbar/*/index.tsx
|
||||
// Related: spec section 2.2
|
||||
import { gql } from "@apollo/client";
|
||||
|
||||
// From widgets/topbar/notification-bell
|
||||
export const GET_NOTIFICATIONS_DOC = gql`
|
||||
query GetNotifications($limit: Int) {
|
||||
notifications(limit: $limit) {
|
||||
id
|
||||
title
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
// From widgets/topbar/user-menu
|
||||
export const GET_CURRENT_USER_DOC = gql`
|
||||
query GetCurrentUser {
|
||||
me {
|
||||
id
|
||||
name
|
||||
email
|
||||
role
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
// From widgets/topbar/global-search
|
||||
export const SEARCH_DOC = gql`
|
||||
query Search($keyword: String!, $limit: Int) {
|
||||
search(keyword: $keyword, limit: $limit) {
|
||||
id
|
||||
type
|
||||
title
|
||||
subtitle
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
// Note: widgets/topbar/locale-switcher has no GraphQL (pure UI state).
|
||||
@@ -0,0 +1,93 @@
|
||||
// Universal domain GraphQL documents
|
||||
// Extracted from widgets/universal/*/index.tsx
|
||||
// Related: spec section 2.2
|
||||
import { gql } from "@apollo/client";
|
||||
|
||||
// From widgets/universal/grades-widget
|
||||
export const GET_GRADES_DOC = gql`
|
||||
query GetGrades($classId: ID!) {
|
||||
grades(classId: $classId) {
|
||||
studentId
|
||||
score
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
// From widgets/universal/homework-widget
|
||||
export const GET_HOMEWORKS_DOC = gql`
|
||||
query GetHomeworks($classId: ID!, $limit: Int) {
|
||||
homeworks(classId: $classId, limit: $limit) {
|
||||
id
|
||||
title
|
||||
dueDate
|
||||
status
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
// From widgets/universal/schedule-widget
|
||||
export const GET_SCHEDULE_DOC = gql`
|
||||
query GetSchedule($classId: ID!, $dayOfWeek: Int) {
|
||||
schedule(classId: $classId, dayOfWeek: $dayOfWeek) {
|
||||
id
|
||||
subject
|
||||
startTime
|
||||
endTime
|
||||
teacherName
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
// From widgets/universal/attendance-widget
|
||||
export const GET_ATTENDANCE_DOC = gql`
|
||||
query GetAttendance($classId: ID!, $termId: ID!) {
|
||||
attendance(classId: $classId, termId: $termId) {
|
||||
present
|
||||
absent
|
||||
late
|
||||
total
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
// From widgets/universal/exams-widget
|
||||
export const GET_EXAMS_DOC = gql`
|
||||
query GetExams($classId: ID!, $limit: Int) {
|
||||
exams(classId: $classId, limit: $limit) {
|
||||
id
|
||||
name
|
||||
examDate
|
||||
subject
|
||||
maxScore
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
// From widgets/universal/notifications-widget
|
||||
export const GET_NOTIFICATIONS_LIST_DOC = gql`
|
||||
query GetNotificationsList($limit: Int, $offset: Int) {
|
||||
notifications(limit: $limit, offset: $offset) {
|
||||
items {
|
||||
id
|
||||
title
|
||||
body
|
||||
createdAt
|
||||
type
|
||||
}
|
||||
total
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
// From widgets/universal/announcements-widget
|
||||
export const GET_ANNOUNCEMENTS_DOC = gql`
|
||||
query GetAnnouncements($limit: Int) {
|
||||
announcements(limit: $limit) {
|
||||
id
|
||||
title
|
||||
body
|
||||
author
|
||||
publishedAt
|
||||
}
|
||||
}
|
||||
`;
|
||||
Reference in New Issue
Block a user