diff --git a/.eslintrc b/.eslintrc index 01241a5..c7ce5a5 100644 --- a/.eslintrc +++ b/.eslintrc @@ -1,11 +1,16 @@ { "plugins": ["unused-imports"], - "extends":["@typescript-eslint/no-unused-vars"], + "extends": ["plugin:@typescript-eslint/recommended"], "rules": { - "unused-imports/no-unused-imports": "error", - "unused-imports/no-unused-vars": [ - "warn", - { "vars": "all", "varsIgnorePattern": "^_", "args": "after-used", "argsIgnorePattern": "^_" } - ] - } -} \ No newline at end of file + "unused-imports/no-unused-imports": "error", + "unused-imports/no-unused-vars": [ + "warn", + { + "vars": "all", + "varsIgnorePattern": "^_", + "args": "after-used", + "argsIgnorePattern": "^_" + } + ] + } +} diff --git a/packages/server/package.json b/packages/server/package.json index c7a2e87..0da156b 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -29,6 +29,7 @@ "nodemailer": "^6.9.8", "openai": "^4.24.7", "passport-local": "^1.0.0", + "puppeteer": "^21.9.0", "reflect-metadata": "^0.2.1", "serialport": "^12.0.0", "type-graphql": "^1.1.1", diff --git a/packages/server/src/app.ts b/packages/server/src/app.ts index d67b1c3..8f7cc12 100644 --- a/packages/server/src/app.ts +++ b/packages/server/src/app.ts @@ -13,7 +13,8 @@ import express from 'express' import { createYoga } from 'graphql-yoga' import mongoose from 'mongoose' import createYogaConfig from './config/yoga' -import { IS_DEV, PHASE } from './constant/common' +import { CLIENT_URL, IS_DEV, PHASE } from './constant/common' +import { captureScreenshot } from './utils/screenshot' const startApp = async () => { console.log('PHASE: ', PHASE) @@ -38,12 +39,35 @@ const startApp = async () => { }) ) + const res = await fetch(`${CLIENT_URL}/styles.css`, { + method: 'GET', + }) + const styleSheet = await res.text() + mongoose.connect(process.env.MONGO_URI as string) const yogaConfig = await createYogaConfig() const yoga = createYoga(yogaConfig) app.use('/graphql', yoga) + // for screenshot + app.post('/screenshot', async (req, res) => { + try { + const htmlContent = req.body.html + const { width, height } = req.body + const screenshotBuffer = await captureScreenshot(htmlContent, { + styleSheet, + width, + height, + }) + res.writeHead(200, { 'Content-Type': 'image/png' }) + res.end(screenshotBuffer, 'binary') + } catch (error) { + console.error('Error capturing screenshot:', error) + res.status(500).send('Internal Server Error') + } + }) + const PORT = process.env.PORT || 4003 app.listen(PORT, () => { diff --git a/packages/server/src/config/yoga.ts b/packages/server/src/config/yoga.ts index cc3a72e..50c1c10 100644 --- a/packages/server/src/config/yoga.ts +++ b/packages/server/src/config/yoga.ts @@ -2,14 +2,19 @@ import type { YogaServerOptions } from 'graphql-yoga' import { buildSchema } from 'type-graphql' import { IS_DEV } from '../constant/common' import renewJwt from '../middlewares/renewJwt.middleware' +import { extractUserId } from '../middlewares/userId.middleware' +import { MandalaChartResolver } from '../resolvers/mandalaChart.resolver' import { RecommendationResolver } from '../resolvers/recommendation.resolver' import { UserResolver } from '../resolvers/user.resolver' import { MyContext } from '../types/common' const createSchema = async () => await buildSchema({ - resolvers: [UserResolver, RecommendationResolver], + resolvers: [UserResolver, MandalaChartResolver, RecommendationResolver], emitSchemaFile: 'src/schema.gql', + validate: { + forbidUnknownValues: false, + }, }) const createYogaConfig = async () => { @@ -20,6 +25,7 @@ const createYogaConfig = async () => { graphiql: IS_DEV, context: async ({ req, res }: MyContext) => { await renewJwt(req, res) + extractUserId(req, res) return { req, res } }, } as YogaServerOptions<{}, {}> diff --git a/packages/server/src/controllers/healthCheck.ts b/packages/server/src/controllers/healthCheck.ts deleted file mode 100644 index 779f946..0000000 --- a/packages/server/src/controllers/healthCheck.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { Request, Response } from 'express' - -const healthCheckController = { - get: (req: Request, res: Response) => { - const name = req.query.name || 'default' - res.send({ name }) - }, -} - -export default healthCheckController diff --git a/packages/server/src/middlewares/userId.middleware.ts b/packages/server/src/middlewares/userId.middleware.ts new file mode 100644 index 0000000..a11bd8a --- /dev/null +++ b/packages/server/src/middlewares/userId.middleware.ts @@ -0,0 +1,29 @@ +import { Request, Response } from 'express' +import * as jwt from 'jsonwebtoken' + +export const extractUserId = (req: Request, res: Response) => { + const token = req.cookies?.token + + if (!token) { + req.userId = null + return + } + + try { + const decoded = jwt.verify( + token, + process.env.JWT_SECRET as string + ) as jwt.JwtPayload + req.userId = decoded.userId + } catch (error) { + req.userId = null + } +} + +declare global { + namespace Express { + interface Request { + userId: string | null + } + } +} diff --git a/packages/server/src/models/MandalaChart.model.ts b/packages/server/src/models/MandalaChart.model.ts new file mode 100644 index 0000000..17889eb --- /dev/null +++ b/packages/server/src/models/MandalaChart.model.ts @@ -0,0 +1,39 @@ +import mongoose, { Document, Schema } from 'mongoose' + +// MandalaCell schema remains the same +const MandalaCellSchema: Schema = new Schema({ + goal: { type: String }, + tasks: [{ type: String }], +}) + +export interface IMandalaCell extends Document { + goal: string + tasks: string[] +} + +const MandalaChartSchema: Schema = new Schema({ + title: { type: String, required: true }, + description: { type: String }, + userId: { type: Schema.Types.ObjectId, ref: 'User' }, + private: { type: Boolean, default: false }, + centerCell: MandalaCellSchema, + createdAt: { type: Date, default: Date.now }, + lastModifiedAt: { type: Date }, + surroundingCells: [MandalaCellSchema], +}) + +export interface IMandalaChart extends Document { + title: string + description?: string + userId: mongoose.Types.ObjectId + private: boolean + centerCell: IMandalaCell + createdAt: Date + lastModifiedAt?: Date + surroundingCells: IMandalaCell[] +} + +export const MandalaChartModel = mongoose.model( + 'MandalaChart', + MandalaChartSchema +) diff --git a/packages/server/src/models/User.model.ts b/packages/server/src/models/User.model.ts index 762e7f1..b045882 100644 --- a/packages/server/src/models/User.model.ts +++ b/packages/server/src/models/User.model.ts @@ -23,6 +23,10 @@ const UserSchema: Schema = new Schema({ }, default: () => ({}), }, + mandalaCharts: { + type: [{ type: Schema.Types.ObjectId, ref: 'MandalaChart' }], + default: [], + }, }) export interface IUser extends Document { @@ -32,6 +36,7 @@ export interface IUser extends Document { emailVerification: TokenInfo resetPassword: TokenInfo purchasedInfo: PurchasedInfo + mandalaCharts: mongoose.Types.ObjectId[] } -export default mongoose.model('User', UserSchema) +export const UserModel = mongoose.model('User', UserSchema) diff --git a/packages/server/src/resolvers/dto/mandalaChart.dto.ts b/packages/server/src/resolvers/dto/mandalaChart.dto.ts new file mode 100644 index 0000000..d3539f1 --- /dev/null +++ b/packages/server/src/resolvers/dto/mandalaChart.dto.ts @@ -0,0 +1,243 @@ +import { + Field, + ID, + InputType, + ObjectType, + createUnionType, + registerEnumType, +} from 'type-graphql' +import { MandalaChart } from '../../types/mandalaChart' +import { BaseError } from './common.dto' + +@InputType() +export class MandalaCellInput { + @Field() + goal: string + + @Field(() => [String]) + tasks: string[] +} + +@InputType() +export class GetMandalaChartInput { + @Field(() => ID) + mandalaChartId: string +} + +@ObjectType() +export class GetMandalaChartSuccess { + @Field(() => MandalaChart) + mandalaChart: MandalaChart +} + +export enum GetMandalaChartFailureType { + CHART_NOT_FOUND = 'CHART_NOT_FOUND', + PRIVATE_CHART = 'PRIVATE_CHART', + SERVER_ERROR = 'SERVER_ERROR', +} + +registerEnumType(GetMandalaChartFailureType, { + name: 'GetMandalaChartFailureType', +}) + +@ObjectType() +export class GetMandalaChartFailure implements BaseError { + @Field(() => GetMandalaChartFailureType) + errorType: GetMandalaChartFailureType +} + +export const GetMandalaChartResponse = createUnionType({ + name: 'GetMandalaChartResponse', + types: () => [GetMandalaChartSuccess, GetMandalaChartFailure] as const, + resolveType: (value: any) => { + if ('mandalaChart' in value) { + return GetMandalaChartSuccess.name + } + return GetMandalaChartFailure.name + }, +}) + +@InputType() +export class GetUserMandalaChartsInput { + @Field(() => ID) + userId: string +} + +@ObjectType() +export class GetUserMandalaChartsSuccess { + @Field(() => [MandalaChart]) + mandalaCharts: MandalaChart[] +} + +export enum GetUserMandalaChartsFailureType { + SERVER_ERROR = 'SERVER_ERROR', +} + +registerEnumType(GetUserMandalaChartsFailureType, { + name: 'GetUserMandalaChartsFailureType', +}) + +@ObjectType() +export class GetUserMandalaChartsFailure implements BaseError { + @Field(() => GetUserMandalaChartsFailureType) + errorType: GetUserMandalaChartsFailureType +} + +export const GetUserMandalaChartsResponse = createUnionType({ + name: 'GetUserMandalaChartsResponse', + types: () => + [GetUserMandalaChartsSuccess, GetUserMandalaChartsFailure] as const, + resolveType: (value: any) => { + if ('errorType' in value) { + return GetUserMandalaChartsFailure.name + } + return GetUserMandalaChartsSuccess.name + }, +}) + +@InputType() +export class CreateMandalaChartInput { + @Field() + title: string + + @Field({ nullable: true }) + description?: string + + @Field() + private: boolean + + @Field(() => MandalaCellInput) + centerCell: MandalaCellInput + + @Field(() => [MandalaCellInput]) + surroundingCells: MandalaCellInput[] +} + +@ObjectType() +export class CreateMandalaChartSuccess { + @Field(() => MandalaChart) + mandalaChart: MandalaChart +} + +export enum CreateMandalaChartFailureType { + NO_TITLE = 'NO_TITLE', + UNAUTHORIZED_ACCESS = 'UNAUTHORIZED_ACCESS', + INVALID_INPUT = 'INVALID_INPUT', + SERVER_ERROR = 'SERVER_ERROR', +} + +registerEnumType(CreateMandalaChartFailureType, { + name: 'CreateMandalaChartFailureType', +}) + +@ObjectType() +export class CreateMandalaChartFailure implements BaseError { + @Field(() => CreateMandalaChartFailureType) + errorType: CreateMandalaChartFailureType +} + +export const CreateMandalaChartResponse = createUnionType({ + name: 'CreateMandalaChartResponse', + types: () => [CreateMandalaChartSuccess, CreateMandalaChartFailure] as const, + resolveType: (value: any) => { + if ('errorType' in value) { + return CreateMandalaChartFailure.name + } + return CreateMandalaChartSuccess.name + }, +}) + +@InputType() +export class UpdateMandalaChartInput extends CreateMandalaChartInput { + @Field(() => ID) + _id: string + + @Field() + title: string + + @Field({ nullable: true }) + description?: string + + @Field() + private: boolean + + @Field(() => MandalaCellInput) + centerCell: MandalaCellInput + + @Field(() => [MandalaCellInput], { nullable: 'itemsAndList' }) + surroundingCells: MandalaCellInput[] +} + +@ObjectType() +export class UpdateMandalaChartSuccess { + @Field(() => MandalaChart) + mandalaChart: MandalaChart +} + +export enum UpdateMandalaChartFailureType { + CHART_NOT_FOUND = 'CHART_NOT_FOUND', + NO_TITLE = 'NO_TITLE', + UNAUTHORIZED_ACCESS = 'UNAUTHORIZED_ACCESS', + INVALID_INPUT = 'INVALID_INPUT', + SERVER_ERROR = 'SERVER_ERROR', +} + +registerEnumType(UpdateMandalaChartFailureType, { + name: 'UpdateMandalaChartFailureType', +}) + +@ObjectType() +export class UpdateMandalaChartFailure implements BaseError { + @Field(() => UpdateMandalaChartFailureType) + errorType: UpdateMandalaChartFailureType +} + +export const UpdateMandalaChartResponse = createUnionType({ + name: 'UpdateMandalaChartResponse', + types: () => [UpdateMandalaChartSuccess, UpdateMandalaChartFailure] as const, + resolveType: (value: any) => { + if ('errorType' in value) { + return UpdateMandalaChartFailure.name + } + return UpdateMandalaChartSuccess.name + }, +}) + +@InputType() +export class DeleteMandalaChartInput { + @Field(() => ID) + mandalaChartId: string +} + +@ObjectType() +export class DeleteMandalaChartSuccess { + @Field(() => ID) + _id: string +} + +export enum DeleteMandalaChartFailureType { + CHART_NOT_FOUND = 'CHART_NOT_FOUND', + UNAUTHORIZED_ACCESS = 'UNAUTHORIZED_ACCESS', + SERVER_ERROR = 'SERVER_ERROR', +} + +registerEnumType(DeleteMandalaChartFailureType, { + name: 'DeleteMandalaChartFailureType', +}) + +@ObjectType() +export class DeleteMandalaChartFailure implements BaseError { + @Field(() => DeleteMandalaChartFailureType) + errorType: DeleteMandalaChartFailureType +} + +export const DeleteMandalaChartResponse = createUnionType({ + name: 'DeleteMandalaChartResponse', + types: () => [DeleteMandalaChartSuccess, DeleteMandalaChartFailure] as const, + resolveType: (value: any) => { + if ('_id' in value) { + return DeleteMandalaChartSuccess.name + } + return DeleteMandalaChartFailure.name + }, +}) diff --git a/packages/server/src/resolvers/mandalaChart.resolver.ts b/packages/server/src/resolvers/mandalaChart.resolver.ts new file mode 100644 index 0000000..f706fbc --- /dev/null +++ b/packages/server/src/resolvers/mandalaChart.resolver.ts @@ -0,0 +1,144 @@ +import { Arg, Ctx, Mutation, Query, Resolver } from 'type-graphql' +import { MandalaChartModel } from '../models/MandalaChart.model' +import { MyContext } from '../types/common' +import { MandalaChart } from '../types/mandalaChart' +import { isMandalaChartInputValid } from '../utils/mandalaChart' +import { + CreateMandalaChartFailureType, + CreateMandalaChartInput, + CreateMandalaChartResponse, + DeleteMandalaChartFailureType, + DeleteMandalaChartInput, + DeleteMandalaChartResponse, + GetMandalaChartFailureType, + GetMandalaChartInput, + GetMandalaChartResponse, + GetUserMandalaChartsInput, + GetUserMandalaChartsResponse, + UpdateMandalaChartFailureType, + UpdateMandalaChartInput, + UpdateMandalaChartResponse, +} from './dto/mandalaChart.dto' + +@Resolver() +export class MandalaChartResolver { + @Query(() => GetMandalaChartResponse) + async getMandalaChart( + @Arg('input') input: GetMandalaChartInput, + @Ctx() ctx: MyContext + ): Promise { + // @ts-ignore + const userId: string | null = ctx.req.userId + + const mandalaChart = await MandalaChartModel.findById(input.mandalaChartId) + .populate('centerCell') + .populate('surroundingCells') + + if (!mandalaChart) { + return { errorType: GetMandalaChartFailureType.CHART_NOT_FOUND } + } + + if (mandalaChart.userId.toString() !== userId && mandalaChart.private) { + return { errorType: GetMandalaChartFailureType.PRIVATE_CHART } + } + + return { mandalaChart: mandalaChart.toJSON() as MandalaChart } + } + + @Query(() => GetUserMandalaChartsResponse) + async getUserMandalaCharts( + @Ctx() ctx: MyContext, + @Arg('input') input: GetUserMandalaChartsInput + ): Promise { + const { userId } = input + // @ts-ignore + const requestUserId: string | null = ctx.req.userId + + const mandalaCharts = await MandalaChartModel.find({ userId }) + const filteredManadalaCharts = mandalaCharts.filter(mandalaChart => + mandalaChart.userId.toString() === requestUserId + ? true + : !mandalaChart.private + ) + + return { + mandalaCharts: filteredManadalaCharts.map( + mandalaChart => mandalaChart.toJSON() as MandalaChart + ), + } + } + + @Mutation(() => CreateMandalaChartResponse) + async createMandalaChart( + @Arg('input') input: CreateMandalaChartInput, + @Ctx() ctx: MyContext + ): Promise { + // @ts-ignore + const userId: string | null = ctx.req.userId + if (!userId) { + return { errorType: CreateMandalaChartFailureType.UNAUTHORIZED_ACCESS } + } + if (!input.title) { + return { errorType: CreateMandalaChartFailureType.NO_TITLE } + } + if (!isMandalaChartInputValid(input.centerCell, input.surroundingCells)) { + return { errorType: CreateMandalaChartFailureType.INVALID_INPUT } + } + const mandalaChart = await MandalaChartModel.create({ ...input, userId }) + return { mandalaChart: mandalaChart.toJSON() as MandalaChart } + } + + @Mutation(() => UpdateMandalaChartResponse) + async updateMandalaChart( + @Arg('input') input: UpdateMandalaChartInput, + @Ctx() ctx: MyContext + ): Promise { + // @ts-ignore + const userId: string | null = ctx.req.userId + + if (!input.title) { + return { errorType: UpdateMandalaChartFailureType.NO_TITLE } + } + if (!isMandalaChartInputValid(input.centerCell, input.surroundingCells)) { + return { errorType: UpdateMandalaChartFailureType.INVALID_INPUT } + } + const candidateChart = await MandalaChartModel.findById(input._id) + if (!candidateChart) { + return { errorType: UpdateMandalaChartFailureType.CHART_NOT_FOUND } + } + if (candidateChart.userId.toString() !== userId) { + return { errorType: UpdateMandalaChartFailureType.UNAUTHORIZED_ACCESS } + } + + const updatedChart = await MandalaChartModel.findByIdAndUpdate( + input._id, + { ...input, updatedAt: Date.now() }, + { new: true } + ) + + return { mandalaChart: updatedChart?.toJSON() as MandalaChart } + } + + @Mutation(() => DeleteMandalaChartResponse) + async deleteMandalaChart( + @Arg('input') input: DeleteMandalaChartInput, + @Ctx() ctx: MyContext + ): Promise { + // @ts-ignore + const userId: string | null = ctx.req.userId + const candidateChart = await MandalaChartModel.findById( + input.mandalaChartId + ) + if (!candidateChart) { + return { errorType: DeleteMandalaChartFailureType.CHART_NOT_FOUND } + } + if (candidateChart.userId.toString() !== userId) { + return { errorType: DeleteMandalaChartFailureType.UNAUTHORIZED_ACCESS } + } + + const deletedChart = (await MandalaChartModel.findByIdAndDelete( + input.mandalaChartId + )) as MandalaChart + return { _id: deletedChart._id } + } +} diff --git a/packages/server/src/resolvers/recommendation.resolver.ts b/packages/server/src/resolvers/recommendation.resolver.ts index 9609ee7..c471e9b 100644 --- a/packages/server/src/resolvers/recommendation.resolver.ts +++ b/packages/server/src/resolvers/recommendation.resolver.ts @@ -1,5 +1,6 @@ import 'reflect-metadata' import { Arg, Query, Resolver } from 'type-graphql' +import { IS_DEV } from '../constant/common' import { RecommendationInNeed, getRecommendations } from '../utils/openai' import { RecommendationFailureType, @@ -22,6 +23,18 @@ export class RecommendationResolver { } try { + if (!IS_DEV) return { recommendations: [] } + if (IS_DEV) { + const make6RandomNumbersBetween0to100 = () => + Array.from({ length: 6 }, () => Math.floor(Math.random() * 100)) + const randomNumbers = make6RandomNumbersBetween0to100() + + return { + recommendations: randomNumbers.map(number => ({ + text: `Recommendation ${number}`, + })), + } + } const { recommendations } = await getRecommendations({ recommendationInNeed: RecommendationInNeed.SubGoals, params: { diff --git a/packages/server/src/resolvers/user.resolver.ts b/packages/server/src/resolvers/user.resolver.ts index 5bd40f0..dd4f45a 100644 --- a/packages/server/src/resolvers/user.resolver.ts +++ b/packages/server/src/resolvers/user.resolver.ts @@ -7,7 +7,7 @@ import { createEmailVerificationTemplate, createResetPasswordTemplate, } from '../constant/nodemailer' -import UserModel, { IUser } from '../models/User.model' +import { IUser, UserModel } from '../models/User.model' import { MyContext } from '../types/common' import { User } from '../types/user' import { isPasswordValid } from '../utils/common' diff --git a/packages/server/src/schema.gql b/packages/server/src/schema.gql index 890926d..97d9885 100644 --- a/packages/server/src/schema.gql +++ b/packages/server/src/schema.gql @@ -3,6 +3,31 @@ # !!! DO NOT MODIFY THIS FILE BY YOURSELF !!! # ----------------------------------------------- +type CreateMandalaChartFailure { + errorType: CreateMandalaChartFailureType! +} + +enum CreateMandalaChartFailureType { + INVALID_INPUT + NO_TITLE + SERVER_ERROR + UNAUTHORIZED_ACCESS +} + +input CreateMandalaChartInput { + centerCell: MandalaCellInput! + description: String + private: Boolean! + surroundingCells: [MandalaCellInput!]! + title: String! +} + +union CreateMandalaChartResponse = CreateMandalaChartFailure | CreateMandalaChartSuccess + +type CreateMandalaChartSuccess { + mandalaChart: MandalaChart! +} + """ The javascript `Date` as string. Type represents date and time as the ISO Date string. """ @@ -23,6 +48,26 @@ type DeleteAccountSuccess { success: Boolean! } +type DeleteMandalaChartFailure { + errorType: DeleteMandalaChartFailureType! +} + +enum DeleteMandalaChartFailureType { + CHART_NOT_FOUND + SERVER_ERROR + UNAUTHORIZED_ACCESS +} + +input DeleteMandalaChartInput { + mandalaChartId: ID! +} + +union DeleteMandalaChartResponse = DeleteMandalaChartFailure | DeleteMandalaChartSuccess + +type DeleteMandalaChartSuccess { + _id: ID! +} + type FindPasswordFailure { errorType: FindPasswordFailureType! } @@ -38,13 +83,77 @@ type FindPasswordSuccess { success: Boolean! } +type GetMandalaChartFailure { + errorType: GetMandalaChartFailureType! +} + +enum GetMandalaChartFailureType { + CHART_NOT_FOUND + PRIVATE_CHART + SERVER_ERROR +} + +input GetMandalaChartInput { + mandalaChartId: ID! +} + +union GetMandalaChartResponse = GetMandalaChartFailure | GetMandalaChartSuccess + +type GetMandalaChartSuccess { + mandalaChart: MandalaChart! +} + +type GetUserMandalaChartsFailure { + errorType: GetUserMandalaChartsFailureType! +} + +enum GetUserMandalaChartsFailureType { + SERVER_ERROR +} + +input GetUserMandalaChartsInput { + userId: ID! +} + +union GetUserMandalaChartsResponse = GetUserMandalaChartsFailure | GetUserMandalaChartsSuccess + +type GetUserMandalaChartsSuccess { + mandalaCharts: [MandalaChart!]! +} + +type MandalaCell { + _id: ID! + goal: String! + tasks: [String!]! +} + +input MandalaCellInput { + goal: String! + tasks: [String!]! +} + +type MandalaChart { + _id: ID! + centerCell: MandalaCell! + createdAt: DateTime! + description: String + lastModifiedAt: DateTime + private: Boolean! + surroundingCells: [MandalaCell!]! + title: String! + userId: ID! +} + type Mutation { + createMandalaChart(input: CreateMandalaChartInput!): CreateMandalaChartResponse! deleteAccount(email: String!): DeleteAccountResponse! + deleteMandalaChart(input: DeleteMandalaChartInput!): DeleteMandalaChartResponse! findPassword(email: String!): FindPasswordResponse! resetPassword(newPassword: String!, newPasswordConfirm: String!, token: String!): ResetPasswordResponse! signIn(email: String!, keepSignedIn: Boolean!, password: String!): SignInResponse! signOut: Boolean! signUp(email: String!, nickname: String!, password: String!, passwordConfirm: String!): SignUpResponse! + updateMandalaChart(input: UpdateMandalaChartInput!): UpdateMandalaChartResponse! verifyEmail(token: String!): VerifyEmailResponse! } @@ -56,7 +165,9 @@ type PurchasedInfo { type Query { checkUser: User + getMandalaChart(input: GetMandalaChartInput!): GetMandalaChartResponse! getUser(_id: String!): User + getUserMandalaCharts(input: GetUserMandalaChartsInput!): GetUserMandalaChartsResponse! recommendationForSubGoals(currentLanguage: String = "en", mainGoal: String!, selectedSubGoals: [String!]): RecommendationResponse! } @@ -131,6 +242,33 @@ type TokenInfo { token: String } +type UpdateMandalaChartFailure { + errorType: UpdateMandalaChartFailureType! +} + +enum UpdateMandalaChartFailureType { + CHART_NOT_FOUND + INVALID_INPUT + NO_TITLE + SERVER_ERROR + UNAUTHORIZED_ACCESS +} + +input UpdateMandalaChartInput { + _id: ID! + centerCell: MandalaCellInput! + description: String + private: Boolean! + surroundingCells: [MandalaCellInput] + title: String! +} + +union UpdateMandalaChartResponse = UpdateMandalaChartFailure | UpdateMandalaChartSuccess + +type UpdateMandalaChartSuccess { + mandalaChart: MandalaChart! +} + type User { _id: ID! createdAt: DateTime! diff --git a/packages/server/src/types/mandalaChart.ts b/packages/server/src/types/mandalaChart.ts new file mode 100644 index 0000000..26a4830 --- /dev/null +++ b/packages/server/src/types/mandalaChart.ts @@ -0,0 +1,44 @@ +import 'reflect-metadata' +import { Field, ID, ObjectType } from 'type-graphql' + +@ObjectType() +export class MandalaCell { + @Field(() => ID) + _id: string + + @Field(() => String) + goal: string + + @Field(() => [String]) + tasks: string[] +} + +@ObjectType() +export class MandalaChart { + @Field(() => ID) + _id: string + + @Field(() => String) + title: string + + @Field(() => String, { nullable: true }) + description?: string + + @Field(() => ID) + userId: string + + @Field(() => Boolean) + private: boolean + + @Field(() => Date) + createdAt: Date + + @Field(() => Date, { nullable: true }) + lastModifiedAt?: Date + + @Field(() => MandalaCell) + centerCell: MandalaCell + + @Field(() => [MandalaCell]) + surroundingCells: MandalaCell[] +} diff --git a/packages/server/src/utils/mandalaChart.ts b/packages/server/src/utils/mandalaChart.ts new file mode 100644 index 0000000..a9fd9eb --- /dev/null +++ b/packages/server/src/utils/mandalaChart.ts @@ -0,0 +1,15 @@ +import { MandalaCellInput } from '../resolvers/dto/mandalaChart.dto' + +export const isMandalaChartInputValid = ( + centerCell: MandalaCellInput, + surroundingCells: MandalaCellInput[] +) => { + if (surroundingCells.length !== 8) return false + + for (let i = 0; i < 8; i++) { + if (surroundingCells[i].tasks.length !== 8) return false + if (centerCell.tasks[i] !== surroundingCells[i].goal) return false + } + + return true +} diff --git a/packages/server/src/utils/screenshot.ts b/packages/server/src/utils/screenshot.ts new file mode 100644 index 0000000..b631dc6 --- /dev/null +++ b/packages/server/src/utils/screenshot.ts @@ -0,0 +1,37 @@ +import puppeteer from 'puppeteer' + +interface Args { + styleSheet: string + width: number + height: number +} + +export const captureScreenshot = async ( + htmlContent: string, + { styleSheet, width, height }: Args +) => { + const browser = await puppeteer.launch() + const page = await browser.newPage() + await page.setViewport({ width, height, deviceScaleFactor: 2 }) + + const htmlWithTailwind = ` + + + + + + ${htmlContent} + + + ` + + await page.setContent(htmlWithTailwind, { waitUntil: 'networkidle0' }) + + const screenshotBuffer = await page.screenshot({ encoding: 'binary' }) + + await browser.close() + + return screenshotBuffer +} diff --git a/packages/web/gql/gql.ts b/packages/web/gql/gql.ts index b90da6d..f27d62f 100644 --- a/packages/web/gql/gql.ts +++ b/packages/web/gql/gql.ts @@ -13,6 +13,10 @@ import { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/ * Therefore it is highly recommended to use the babel or swc plugin for production. */ const documents = { + "\n query GetRecommendationForSubGoals(\n $mainGoal: String!\n $selectedSubGoals: [String!]\n $currentLanguage: String!\n ) {\n recommendationForSubGoals(\n mainGoal: $mainGoal\n selectedSubGoals: $selectedSubGoals\n currentLanguage: $currentLanguage\n ) {\n ... on RecommendationSuccess {\n recommendations {\n text\n }\n }\n ... on RecommendationFailure {\n errorType\n }\n }\n }\n": types.GetRecommendationForSubGoalsDocument, + "\n mutation CreateMandalaChart($input: CreateMandalaChartInput!) {\n createMandalaChart(input: $input) {\n ... on CreateMandalaChartSuccess {\n mandalaChart {\n _id\n }\n }\n ... on CreateMandalaChartFailure {\n errorType\n }\n }\n }\n": types.CreateMandalaChartDocument, + "\n mutation UpdateMandalaChart($input: UpdateMandalaChartInput!) {\n updateMandalaChart(input: $input) {\n ... on UpdateMandalaChartSuccess {\n mandalaChart {\n _id\n }\n }\n ... on UpdateMandalaChartFailure {\n errorType\n }\n }\n }\n": types.UpdateMandalaChartDocument, + "\n mutation DeleteMandalaChart($input: DeleteMandalaChartInput!) {\n deleteMandalaChart(input: $input) {\n ... on DeleteMandalaChartSuccess {\n _id\n }\n ... on DeleteMandalaChartFailure {\n errorType\n }\n }\n }\n": types.DeleteMandalaChartDocument, "\n mutation SignOut {\n signOut\n }\n": types.SignOutDocument, "\n mutation DeleteAccount($email: String!) {\n deleteAccount(email: $email) {\n ... on DeleteAccountSuccess {\n success\n }\n ... on DeleteAccountFailure {\n errorType\n }\n }\n }\n": types.DeleteAccountDocument, "\n mutation FindPassword($email: String!) {\n findPassword(email: $email) {\n ... on FindPasswordSuccess {\n success\n }\n ... on FindPasswordFailure {\n errorType\n }\n }\n }\n": types.FindPasswordDocument, @@ -20,7 +24,8 @@ const documents = { "\n mutation SignIn(\n $email: String!\n $password: String!\n $keepSignedIn: Boolean!\n ) {\n signIn(email: $email, password: $password, keepSignedIn: $keepSignedIn) {\n ... on SignInSuccess {\n token\n user {\n _id\n email\n nickname\n createdAt\n emailVerification {\n isVerified\n token\n expiresAt\n }\n resetPassword {\n token\n expiresAt\n isVerified\n }\n purchasedInfo {\n isPurchased\n purchasedAt\n expiresAt\n }\n }\n }\n ... on SignInFailure {\n errorType\n }\n }\n }\n": types.SignInDocument, "\n mutation SignUp(\n $email: String!\n $password: String!\n $passwordConfirm: String!\n $nickname: String!\n ) {\n signUp(\n email: $email\n password: $password\n passwordConfirm: $passwordConfirm\n nickname: $nickname\n ) {\n ... on SignUpSuccess {\n isMailSent\n }\n ... on SignUpFailure {\n errorType\n }\n }\n }\n": types.SignUpDocument, "\n mutation VerifyEmail($token: String!) {\n verifyEmail(token: $token) {\n ... on VerifyEmailSuccess {\n success\n }\n ... on VerifyEmailFailure {\n errorType\n }\n }\n }\n": types.VerifyEmailDocument, - "\n query GetRecommendationForSubGoals(\n $mainGoal: String!\n $selectedSubGoals: [String!]\n $currentLanguage: String!\n ) {\n recommendationForSubGoals(\n mainGoal: $mainGoal\n selectedSubGoals: $selectedSubGoals\n currentLanguage: $currentLanguage\n ) {\n ... on RecommendationSuccess {\n recommendations {\n text\n }\n }\n ... on RecommendationFailure {\n errorType\n }\n }\n }\n": types.GetRecommendationForSubGoalsDocument, + "\n query GetMandalaChart($input: GetMandalaChartInput!) {\n getMandalaChart(input: $input) {\n ... on GetMandalaChartSuccess {\n mandalaChart {\n _id\n title\n description\n private\n createdAt\n lastModifiedAt\n centerCell {\n goal\n tasks\n }\n surroundingCells {\n goal\n tasks\n }\n }\n }\n ... on GetMandalaChartFailure {\n errorType\n }\n }\n }\n": types.GetMandalaChartDocument, + "\n query GetUserMandalaCharts($input: GetUserMandalaChartsInput!) {\n getUserMandalaCharts(input: $input) {\n ... on GetUserMandalaChartsSuccess {\n mandalaCharts {\n _id\n title\n description\n private\n createdAt\n lastModifiedAt\n }\n }\n ... on GetUserMandalaChartsFailure {\n errorType\n }\n }\n }\n": types.GetUserMandalaChartsDocument, }; /** @@ -37,6 +42,22 @@ const documents = { */ export function graphql(source: string): unknown; +/** + * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. + */ +export function graphql(source: "\n query GetRecommendationForSubGoals(\n $mainGoal: String!\n $selectedSubGoals: [String!]\n $currentLanguage: String!\n ) {\n recommendationForSubGoals(\n mainGoal: $mainGoal\n selectedSubGoals: $selectedSubGoals\n currentLanguage: $currentLanguage\n ) {\n ... on RecommendationSuccess {\n recommendations {\n text\n }\n }\n ... on RecommendationFailure {\n errorType\n }\n }\n }\n"): (typeof documents)["\n query GetRecommendationForSubGoals(\n $mainGoal: String!\n $selectedSubGoals: [String!]\n $currentLanguage: String!\n ) {\n recommendationForSubGoals(\n mainGoal: $mainGoal\n selectedSubGoals: $selectedSubGoals\n currentLanguage: $currentLanguage\n ) {\n ... on RecommendationSuccess {\n recommendations {\n text\n }\n }\n ... on RecommendationFailure {\n errorType\n }\n }\n }\n"]; +/** + * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. + */ +export function graphql(source: "\n mutation CreateMandalaChart($input: CreateMandalaChartInput!) {\n createMandalaChart(input: $input) {\n ... on CreateMandalaChartSuccess {\n mandalaChart {\n _id\n }\n }\n ... on CreateMandalaChartFailure {\n errorType\n }\n }\n }\n"): (typeof documents)["\n mutation CreateMandalaChart($input: CreateMandalaChartInput!) {\n createMandalaChart(input: $input) {\n ... on CreateMandalaChartSuccess {\n mandalaChart {\n _id\n }\n }\n ... on CreateMandalaChartFailure {\n errorType\n }\n }\n }\n"]; +/** + * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. + */ +export function graphql(source: "\n mutation UpdateMandalaChart($input: UpdateMandalaChartInput!) {\n updateMandalaChart(input: $input) {\n ... on UpdateMandalaChartSuccess {\n mandalaChart {\n _id\n }\n }\n ... on UpdateMandalaChartFailure {\n errorType\n }\n }\n }\n"): (typeof documents)["\n mutation UpdateMandalaChart($input: UpdateMandalaChartInput!) {\n updateMandalaChart(input: $input) {\n ... on UpdateMandalaChartSuccess {\n mandalaChart {\n _id\n }\n }\n ... on UpdateMandalaChartFailure {\n errorType\n }\n }\n }\n"]; +/** + * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. + */ +export function graphql(source: "\n mutation DeleteMandalaChart($input: DeleteMandalaChartInput!) {\n deleteMandalaChart(input: $input) {\n ... on DeleteMandalaChartSuccess {\n _id\n }\n ... on DeleteMandalaChartFailure {\n errorType\n }\n }\n }\n"): (typeof documents)["\n mutation DeleteMandalaChart($input: DeleteMandalaChartInput!) {\n deleteMandalaChart(input: $input) {\n ... on DeleteMandalaChartSuccess {\n _id\n }\n ... on DeleteMandalaChartFailure {\n errorType\n }\n }\n }\n"]; /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ @@ -68,7 +89,11 @@ export function graphql(source: "\n mutation VerifyEmail($token: String!) {\n /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ -export function graphql(source: "\n query GetRecommendationForSubGoals(\n $mainGoal: String!\n $selectedSubGoals: [String!]\n $currentLanguage: String!\n ) {\n recommendationForSubGoals(\n mainGoal: $mainGoal\n selectedSubGoals: $selectedSubGoals\n currentLanguage: $currentLanguage\n ) {\n ... on RecommendationSuccess {\n recommendations {\n text\n }\n }\n ... on RecommendationFailure {\n errorType\n }\n }\n }\n"): (typeof documents)["\n query GetRecommendationForSubGoals(\n $mainGoal: String!\n $selectedSubGoals: [String!]\n $currentLanguage: String!\n ) {\n recommendationForSubGoals(\n mainGoal: $mainGoal\n selectedSubGoals: $selectedSubGoals\n currentLanguage: $currentLanguage\n ) {\n ... on RecommendationSuccess {\n recommendations {\n text\n }\n }\n ... on RecommendationFailure {\n errorType\n }\n }\n }\n"]; +export function graphql(source: "\n query GetMandalaChart($input: GetMandalaChartInput!) {\n getMandalaChart(input: $input) {\n ... on GetMandalaChartSuccess {\n mandalaChart {\n _id\n title\n description\n private\n createdAt\n lastModifiedAt\n centerCell {\n goal\n tasks\n }\n surroundingCells {\n goal\n tasks\n }\n }\n }\n ... on GetMandalaChartFailure {\n errorType\n }\n }\n }\n"): (typeof documents)["\n query GetMandalaChart($input: GetMandalaChartInput!) {\n getMandalaChart(input: $input) {\n ... on GetMandalaChartSuccess {\n mandalaChart {\n _id\n title\n description\n private\n createdAt\n lastModifiedAt\n centerCell {\n goal\n tasks\n }\n surroundingCells {\n goal\n tasks\n }\n }\n }\n ... on GetMandalaChartFailure {\n errorType\n }\n }\n }\n"]; +/** + * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. + */ +export function graphql(source: "\n query GetUserMandalaCharts($input: GetUserMandalaChartsInput!) {\n getUserMandalaCharts(input: $input) {\n ... on GetUserMandalaChartsSuccess {\n mandalaCharts {\n _id\n title\n description\n private\n createdAt\n lastModifiedAt\n }\n }\n ... on GetUserMandalaChartsFailure {\n errorType\n }\n }\n }\n"): (typeof documents)["\n query GetUserMandalaCharts($input: GetUserMandalaChartsInput!) {\n getUserMandalaCharts(input: $input) {\n ... on GetUserMandalaChartsSuccess {\n mandalaCharts {\n _id\n title\n description\n private\n createdAt\n lastModifiedAt\n }\n }\n ... on GetUserMandalaChartsFailure {\n errorType\n }\n }\n }\n"]; export function graphql(source: string) { return (documents as any)[source] ?? {}; diff --git a/packages/web/gql/graphql.ts b/packages/web/gql/graphql.ts index ccd965b..b0bcd61 100644 --- a/packages/web/gql/graphql.ts +++ b/packages/web/gql/graphql.ts @@ -18,6 +18,33 @@ export type Scalars = { DateTime: { input: string; output: string; } }; +export type CreateMandalaChartFailure = { + __typename?: 'CreateMandalaChartFailure'; + errorType: CreateMandalaChartFailureType; +}; + +export enum CreateMandalaChartFailureType { + InvalidInput = 'INVALID_INPUT', + NoTitle = 'NO_TITLE', + ServerError = 'SERVER_ERROR', + UnauthorizedAccess = 'UNAUTHORIZED_ACCESS' +} + +export type CreateMandalaChartInput = { + centerCell: MandalaCellInput; + description?: InputMaybe; + private: Scalars['Boolean']['input']; + surroundingCells: Array; + title: Scalars['String']['input']; +}; + +export type CreateMandalaChartResponse = CreateMandalaChartFailure | CreateMandalaChartSuccess; + +export type CreateMandalaChartSuccess = { + __typename?: 'CreateMandalaChartSuccess'; + mandalaChart: MandalaChart; +}; + export type DeleteAccountFailure = { __typename?: 'DeleteAccountFailure'; errorType: DeleteAccountFailureType; @@ -35,6 +62,28 @@ export type DeleteAccountSuccess = { success: Scalars['Boolean']['output']; }; +export type DeleteMandalaChartFailure = { + __typename?: 'DeleteMandalaChartFailure'; + errorType: DeleteMandalaChartFailureType; +}; + +export enum DeleteMandalaChartFailureType { + ChartNotFound = 'CHART_NOT_FOUND', + ServerError = 'SERVER_ERROR', + UnauthorizedAccess = 'UNAUTHORIZED_ACCESS' +} + +export type DeleteMandalaChartInput = { + mandalaChartId: Scalars['ID']['input']; +}; + +export type DeleteMandalaChartResponse = DeleteMandalaChartFailure | DeleteMandalaChartSuccess; + +export type DeleteMandalaChartSuccess = { + __typename?: 'DeleteMandalaChartSuccess'; + _id: Scalars['ID']['output']; +}; + export type FindPasswordFailure = { __typename?: 'FindPasswordFailure'; errorType: FindPasswordFailureType; @@ -52,23 +101,103 @@ export type FindPasswordSuccess = { success: Scalars['Boolean']['output']; }; +export type GetMandalaChartFailure = { + __typename?: 'GetMandalaChartFailure'; + errorType: GetMandalaChartFailureType; +}; + +export enum GetMandalaChartFailureType { + ChartNotFound = 'CHART_NOT_FOUND', + PrivateChart = 'PRIVATE_CHART', + ServerError = 'SERVER_ERROR' +} + +export type GetMandalaChartInput = { + mandalaChartId: Scalars['ID']['input']; +}; + +export type GetMandalaChartResponse = GetMandalaChartFailure | GetMandalaChartSuccess; + +export type GetMandalaChartSuccess = { + __typename?: 'GetMandalaChartSuccess'; + mandalaChart: MandalaChart; +}; + +export type GetUserMandalaChartsFailure = { + __typename?: 'GetUserMandalaChartsFailure'; + errorType: GetUserMandalaChartsFailureType; +}; + +export enum GetUserMandalaChartsFailureType { + ServerError = 'SERVER_ERROR' +} + +export type GetUserMandalaChartsInput = { + userId: Scalars['ID']['input']; +}; + +export type GetUserMandalaChartsResponse = GetUserMandalaChartsFailure | GetUserMandalaChartsSuccess; + +export type GetUserMandalaChartsSuccess = { + __typename?: 'GetUserMandalaChartsSuccess'; + mandalaCharts: Array; +}; + +export type MandalaCell = { + __typename?: 'MandalaCell'; + _id: Scalars['ID']['output']; + goal: Scalars['String']['output']; + tasks: Array; +}; + +export type MandalaCellInput = { + goal: Scalars['String']['input']; + tasks: Array; +}; + +export type MandalaChart = { + __typename?: 'MandalaChart'; + _id: Scalars['ID']['output']; + centerCell: MandalaCell; + createdAt: Scalars['DateTime']['output']; + description: Maybe; + lastModifiedAt: Maybe; + private: Scalars['Boolean']['output']; + surroundingCells: Array; + title: Scalars['String']['output']; + userId: Scalars['ID']['output']; +}; + export type Mutation = { __typename?: 'Mutation'; + createMandalaChart: CreateMandalaChartResponse; deleteAccount: DeleteAccountResponse; + deleteMandalaChart: DeleteMandalaChartResponse; findPassword: FindPasswordResponse; resetPassword: ResetPasswordResponse; signIn: SignInResponse; signOut: Scalars['Boolean']['output']; signUp: SignUpResponse; + updateMandalaChart: UpdateMandalaChartResponse; verifyEmail: VerifyEmailResponse; }; +export type MutationCreateMandalaChartArgs = { + input: CreateMandalaChartInput; +}; + + export type MutationDeleteAccountArgs = { email: Scalars['String']['input']; }; +export type MutationDeleteMandalaChartArgs = { + input: DeleteMandalaChartInput; +}; + + export type MutationFindPasswordArgs = { email: Scalars['String']['input']; }; @@ -96,6 +225,11 @@ export type MutationSignUpArgs = { }; +export type MutationUpdateMandalaChartArgs = { + input: UpdateMandalaChartInput; +}; + + export type MutationVerifyEmailArgs = { token: Scalars['String']['input']; }; @@ -110,16 +244,28 @@ export type PurchasedInfo = { export type Query = { __typename?: 'Query'; checkUser: Maybe; + getMandalaChart: GetMandalaChartResponse; getUser: Maybe; + getUserMandalaCharts: GetUserMandalaChartsResponse; recommendationForSubGoals: RecommendationResponse; }; +export type QueryGetMandalaChartArgs = { + input: GetMandalaChartInput; +}; + + export type QueryGetUserArgs = { _id: Scalars['String']['input']; }; +export type QueryGetUserMandalaChartsArgs = { + input: GetUserMandalaChartsInput; +}; + + export type QueryRecommendationForSubGoalsArgs = { currentLanguage?: InputMaybe; mainGoal: Scalars['String']['input']; @@ -207,6 +353,35 @@ export type TokenInfo = { token: Maybe; }; +export type UpdateMandalaChartFailure = { + __typename?: 'UpdateMandalaChartFailure'; + errorType: UpdateMandalaChartFailureType; +}; + +export enum UpdateMandalaChartFailureType { + ChartNotFound = 'CHART_NOT_FOUND', + InvalidInput = 'INVALID_INPUT', + NoTitle = 'NO_TITLE', + ServerError = 'SERVER_ERROR', + UnauthorizedAccess = 'UNAUTHORIZED_ACCESS' +} + +export type UpdateMandalaChartInput = { + _id: Scalars['ID']['input']; + centerCell: MandalaCellInput; + description?: InputMaybe; + private: Scalars['Boolean']['input']; + surroundingCells?: InputMaybe>>; + title: Scalars['String']['input']; +}; + +export type UpdateMandalaChartResponse = UpdateMandalaChartFailure | UpdateMandalaChartSuccess; + +export type UpdateMandalaChartSuccess = { + __typename?: 'UpdateMandalaChartSuccess'; + mandalaChart: MandalaChart; +}; + export type User = { __typename?: 'User'; _id: Scalars['ID']['output']; @@ -235,6 +410,36 @@ export type VerifyEmailSuccess = { success: Scalars['Boolean']['output']; }; +export type GetRecommendationForSubGoalsQueryVariables = Exact<{ + mainGoal: Scalars['String']['input']; + selectedSubGoals?: InputMaybe | Scalars['String']['input']>; + currentLanguage: Scalars['String']['input']; +}>; + + +export type GetRecommendationForSubGoalsQuery = { __typename?: 'Query', recommendationForSubGoals: { __typename?: 'RecommendationFailure', errorType: RecommendationFailureType } | { __typename?: 'RecommendationSuccess', recommendations: Array<{ __typename?: 'Recommendation', text: string }> } }; + +export type CreateMandalaChartMutationVariables = Exact<{ + input: CreateMandalaChartInput; +}>; + + +export type CreateMandalaChartMutation = { __typename?: 'Mutation', createMandalaChart: { __typename?: 'CreateMandalaChartFailure', errorType: CreateMandalaChartFailureType } | { __typename?: 'CreateMandalaChartSuccess', mandalaChart: { __typename?: 'MandalaChart', _id: string } } }; + +export type UpdateMandalaChartMutationVariables = Exact<{ + input: UpdateMandalaChartInput; +}>; + + +export type UpdateMandalaChartMutation = { __typename?: 'Mutation', updateMandalaChart: { __typename?: 'UpdateMandalaChartFailure', errorType: UpdateMandalaChartFailureType } | { __typename?: 'UpdateMandalaChartSuccess', mandalaChart: { __typename?: 'MandalaChart', _id: string } } }; + +export type DeleteMandalaChartMutationVariables = Exact<{ + input: DeleteMandalaChartInput; +}>; + + +export type DeleteMandalaChartMutation = { __typename?: 'Mutation', deleteMandalaChart: { __typename?: 'DeleteMandalaChartFailure', errorType: DeleteMandalaChartFailureType } | { __typename?: 'DeleteMandalaChartSuccess', _id: string } }; + export type SignOutMutationVariables = Exact<{ [key: string]: never; }>; @@ -289,16 +494,25 @@ export type VerifyEmailMutationVariables = Exact<{ export type VerifyEmailMutation = { __typename?: 'Mutation', verifyEmail: { __typename?: 'VerifyEmailFailure', errorType: VerifyEmailFailureType } | { __typename?: 'VerifyEmailSuccess', success: boolean } }; -export type GetRecommendationForSubGoalsQueryVariables = Exact<{ - mainGoal: Scalars['String']['input']; - selectedSubGoals?: InputMaybe | Scalars['String']['input']>; - currentLanguage: Scalars['String']['input']; +export type GetMandalaChartQueryVariables = Exact<{ + input: GetMandalaChartInput; }>; -export type GetRecommendationForSubGoalsQuery = { __typename?: 'Query', recommendationForSubGoals: { __typename?: 'RecommendationFailure', errorType: RecommendationFailureType } | { __typename?: 'RecommendationSuccess', recommendations: Array<{ __typename?: 'Recommendation', text: string }> } }; +export type GetMandalaChartQuery = { __typename?: 'Query', getMandalaChart: { __typename?: 'GetMandalaChartFailure', errorType: GetMandalaChartFailureType } | { __typename?: 'GetMandalaChartSuccess', mandalaChart: { __typename?: 'MandalaChart', _id: string, title: string, description: string | null, private: boolean, createdAt: string, lastModifiedAt: string | null, centerCell: { __typename?: 'MandalaCell', goal: string, tasks: Array }, surroundingCells: Array<{ __typename?: 'MandalaCell', goal: string, tasks: Array }> } } }; + +export type GetUserMandalaChartsQueryVariables = Exact<{ + input: GetUserMandalaChartsInput; +}>; + + +export type GetUserMandalaChartsQuery = { __typename?: 'Query', getUserMandalaCharts: { __typename?: 'GetUserMandalaChartsFailure', errorType: GetUserMandalaChartsFailureType } | { __typename?: 'GetUserMandalaChartsSuccess', mandalaCharts: Array<{ __typename?: 'MandalaChart', _id: string, title: string, description: string | null, private: boolean, createdAt: string, lastModifiedAt: string | null }> } }; +export const GetRecommendationForSubGoalsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetRecommendationForSubGoals"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"mainGoal"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"selectedSubGoals"}},"type":{"kind":"ListType","type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"currentLanguage"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"recommendationForSubGoals"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"mainGoal"},"value":{"kind":"Variable","name":{"kind":"Name","value":"mainGoal"}}},{"kind":"Argument","name":{"kind":"Name","value":"selectedSubGoals"},"value":{"kind":"Variable","name":{"kind":"Name","value":"selectedSubGoals"}}},{"kind":"Argument","name":{"kind":"Name","value":"currentLanguage"},"value":{"kind":"Variable","name":{"kind":"Name","value":"currentLanguage"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"RecommendationSuccess"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"recommendations"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"text"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"RecommendationFailure"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"errorType"}}]}}]}}]}}]} as unknown as DocumentNode; +export const CreateMandalaChartDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateMandalaChart"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"CreateMandalaChartInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createMandalaChart"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"CreateMandalaChartSuccess"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"mandalaChart"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"_id"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"CreateMandalaChartFailure"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"errorType"}}]}}]}}]}}]} as unknown as DocumentNode; +export const UpdateMandalaChartDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpdateMandalaChart"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UpdateMandalaChartInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"updateMandalaChart"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"UpdateMandalaChartSuccess"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"mandalaChart"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"_id"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"UpdateMandalaChartFailure"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"errorType"}}]}}]}}]}}]} as unknown as DocumentNode; +export const DeleteMandalaChartDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"DeleteMandalaChart"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"DeleteMandalaChartInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"deleteMandalaChart"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"DeleteMandalaChartSuccess"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"_id"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"DeleteMandalaChartFailure"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"errorType"}}]}}]}}]}}]} as unknown as DocumentNode; export const SignOutDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"SignOut"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"signOut"}}]}}]} as unknown as DocumentNode; export const DeleteAccountDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"DeleteAccount"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"email"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"deleteAccount"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"email"},"value":{"kind":"Variable","name":{"kind":"Name","value":"email"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"DeleteAccountSuccess"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"success"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"DeleteAccountFailure"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"errorType"}}]}}]}}]}}]} as unknown as DocumentNode; export const FindPasswordDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"FindPassword"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"email"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"findPassword"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"email"},"value":{"kind":"Variable","name":{"kind":"Name","value":"email"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"FindPasswordSuccess"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"success"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"FindPasswordFailure"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"errorType"}}]}}]}}]}}]} as unknown as DocumentNode; @@ -306,4 +520,5 @@ export const ResetPasswordDocument = {"kind":"Document","definitions":[{"kind":" export const SignInDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"SignIn"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"email"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"password"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"keepSignedIn"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Boolean"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"signIn"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"email"},"value":{"kind":"Variable","name":{"kind":"Name","value":"email"}}},{"kind":"Argument","name":{"kind":"Name","value":"password"},"value":{"kind":"Variable","name":{"kind":"Name","value":"password"}}},{"kind":"Argument","name":{"kind":"Name","value":"keepSignedIn"},"value":{"kind":"Variable","name":{"kind":"Name","value":"keepSignedIn"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SignInSuccess"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"token"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"_id"}},{"kind":"Field","name":{"kind":"Name","value":"email"}},{"kind":"Field","name":{"kind":"Name","value":"nickname"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"emailVerification"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"isVerified"}},{"kind":"Field","name":{"kind":"Name","value":"token"}},{"kind":"Field","name":{"kind":"Name","value":"expiresAt"}}]}},{"kind":"Field","name":{"kind":"Name","value":"resetPassword"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"token"}},{"kind":"Field","name":{"kind":"Name","value":"expiresAt"}},{"kind":"Field","name":{"kind":"Name","value":"isVerified"}}]}},{"kind":"Field","name":{"kind":"Name","value":"purchasedInfo"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"isPurchased"}},{"kind":"Field","name":{"kind":"Name","value":"purchasedAt"}},{"kind":"Field","name":{"kind":"Name","value":"expiresAt"}}]}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SignInFailure"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"errorType"}}]}}]}}]}}]} as unknown as DocumentNode; export const SignUpDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"SignUp"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"email"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"password"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"passwordConfirm"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"nickname"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"signUp"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"email"},"value":{"kind":"Variable","name":{"kind":"Name","value":"email"}}},{"kind":"Argument","name":{"kind":"Name","value":"password"},"value":{"kind":"Variable","name":{"kind":"Name","value":"password"}}},{"kind":"Argument","name":{"kind":"Name","value":"passwordConfirm"},"value":{"kind":"Variable","name":{"kind":"Name","value":"passwordConfirm"}}},{"kind":"Argument","name":{"kind":"Name","value":"nickname"},"value":{"kind":"Variable","name":{"kind":"Name","value":"nickname"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SignUpSuccess"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"isMailSent"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SignUpFailure"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"errorType"}}]}}]}}]}}]} as unknown as DocumentNode; export const VerifyEmailDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"VerifyEmail"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"token"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"verifyEmail"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"token"},"value":{"kind":"Variable","name":{"kind":"Name","value":"token"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"VerifyEmailSuccess"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"success"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"VerifyEmailFailure"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"errorType"}}]}}]}}]}}]} as unknown as DocumentNode; -export const GetRecommendationForSubGoalsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetRecommendationForSubGoals"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"mainGoal"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"selectedSubGoals"}},"type":{"kind":"ListType","type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"currentLanguage"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"recommendationForSubGoals"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"mainGoal"},"value":{"kind":"Variable","name":{"kind":"Name","value":"mainGoal"}}},{"kind":"Argument","name":{"kind":"Name","value":"selectedSubGoals"},"value":{"kind":"Variable","name":{"kind":"Name","value":"selectedSubGoals"}}},{"kind":"Argument","name":{"kind":"Name","value":"currentLanguage"},"value":{"kind":"Variable","name":{"kind":"Name","value":"currentLanguage"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"RecommendationSuccess"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"recommendations"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"text"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"RecommendationFailure"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"errorType"}}]}}]}}]}}]} as unknown as DocumentNode; \ No newline at end of file +export const GetMandalaChartDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetMandalaChart"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"GetMandalaChartInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"getMandalaChart"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"GetMandalaChartSuccess"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"mandalaChart"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"_id"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"private"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"lastModifiedAt"}},{"kind":"Field","name":{"kind":"Name","value":"centerCell"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"goal"}},{"kind":"Field","name":{"kind":"Name","value":"tasks"}}]}},{"kind":"Field","name":{"kind":"Name","value":"surroundingCells"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"goal"}},{"kind":"Field","name":{"kind":"Name","value":"tasks"}}]}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"GetMandalaChartFailure"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"errorType"}}]}}]}}]}}]} as unknown as DocumentNode; +export const GetUserMandalaChartsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetUserMandalaCharts"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"GetUserMandalaChartsInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"getUserMandalaCharts"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"GetUserMandalaChartsSuccess"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"mandalaCharts"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"_id"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"private"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"lastModifiedAt"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"GetUserMandalaChartsFailure"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"errorType"}}]}}]}}]}}]} as unknown as DocumentNode; \ No newline at end of file diff --git a/packages/web/libs/apollo/typePolicies.helper.ts b/packages/web/libs/apollo/typePolicies.helper.ts index 9c162a5..edf3182 100644 --- a/packages/web/libs/apollo/typePolicies.helper.ts +++ b/packages/web/libs/apollo/typePolicies.helper.ts @@ -1,4 +1,12 @@ import { FieldPolicy, FieldReadFunction, TypePolicies, TypePolicy } from '@apollo/client/cache'; +export type CreateMandalaChartFailureKeySpecifier = ('errorType' | CreateMandalaChartFailureKeySpecifier)[]; +export type CreateMandalaChartFailureFieldPolicy = { + errorType?: FieldPolicy | FieldReadFunction +}; +export type CreateMandalaChartSuccessKeySpecifier = ('mandalaChart' | CreateMandalaChartSuccessKeySpecifier)[]; +export type CreateMandalaChartSuccessFieldPolicy = { + mandalaChart?: FieldPolicy | FieldReadFunction +}; export type DeleteAccountFailureKeySpecifier = ('errorType' | DeleteAccountFailureKeySpecifier)[]; export type DeleteAccountFailureFieldPolicy = { errorType?: FieldPolicy | FieldReadFunction @@ -7,6 +15,14 @@ export type DeleteAccountSuccessKeySpecifier = ('success' | DeleteAccountSuccess export type DeleteAccountSuccessFieldPolicy = { success?: FieldPolicy | FieldReadFunction }; +export type DeleteMandalaChartFailureKeySpecifier = ('errorType' | DeleteMandalaChartFailureKeySpecifier)[]; +export type DeleteMandalaChartFailureFieldPolicy = { + errorType?: FieldPolicy | FieldReadFunction +}; +export type DeleteMandalaChartSuccessKeySpecifier = ('_id' | DeleteMandalaChartSuccessKeySpecifier)[]; +export type DeleteMandalaChartSuccessFieldPolicy = { + _id?: FieldPolicy | FieldReadFunction +}; export type FindPasswordFailureKeySpecifier = ('errorType' | FindPasswordFailureKeySpecifier)[]; export type FindPasswordFailureFieldPolicy = { errorType?: FieldPolicy | FieldReadFunction @@ -15,14 +31,51 @@ export type FindPasswordSuccessKeySpecifier = ('success' | FindPasswordSuccessKe export type FindPasswordSuccessFieldPolicy = { success?: FieldPolicy | FieldReadFunction }; -export type MutationKeySpecifier = ('deleteAccount' | 'findPassword' | 'resetPassword' | 'signIn' | 'signOut' | 'signUp' | 'verifyEmail' | MutationKeySpecifier)[]; +export type GetMandalaChartFailureKeySpecifier = ('errorType' | GetMandalaChartFailureKeySpecifier)[]; +export type GetMandalaChartFailureFieldPolicy = { + errorType?: FieldPolicy | FieldReadFunction +}; +export type GetMandalaChartSuccessKeySpecifier = ('mandalaChart' | GetMandalaChartSuccessKeySpecifier)[]; +export type GetMandalaChartSuccessFieldPolicy = { + mandalaChart?: FieldPolicy | FieldReadFunction +}; +export type GetUserMandalaChartsFailureKeySpecifier = ('errorType' | GetUserMandalaChartsFailureKeySpecifier)[]; +export type GetUserMandalaChartsFailureFieldPolicy = { + errorType?: FieldPolicy | FieldReadFunction +}; +export type GetUserMandalaChartsSuccessKeySpecifier = ('mandalaCharts' | GetUserMandalaChartsSuccessKeySpecifier)[]; +export type GetUserMandalaChartsSuccessFieldPolicy = { + mandalaCharts?: FieldPolicy | FieldReadFunction +}; +export type MandalaCellKeySpecifier = ('_id' | 'goal' | 'tasks' | MandalaCellKeySpecifier)[]; +export type MandalaCellFieldPolicy = { + _id?: FieldPolicy | FieldReadFunction, + goal?: FieldPolicy | FieldReadFunction, + tasks?: FieldPolicy | FieldReadFunction +}; +export type MandalaChartKeySpecifier = ('_id' | 'centerCell' | 'createdAt' | 'description' | 'lastModifiedAt' | 'private' | 'surroundingCells' | 'title' | 'userId' | MandalaChartKeySpecifier)[]; +export type MandalaChartFieldPolicy = { + _id?: FieldPolicy | FieldReadFunction, + centerCell?: FieldPolicy | FieldReadFunction, + createdAt?: FieldPolicy | FieldReadFunction, + description?: FieldPolicy | FieldReadFunction, + lastModifiedAt?: FieldPolicy | FieldReadFunction, + private?: FieldPolicy | FieldReadFunction, + surroundingCells?: FieldPolicy | FieldReadFunction, + title?: FieldPolicy | FieldReadFunction, + userId?: FieldPolicy | FieldReadFunction +}; +export type MutationKeySpecifier = ('createMandalaChart' | 'deleteAccount' | 'deleteMandalaChart' | 'findPassword' | 'resetPassword' | 'signIn' | 'signOut' | 'signUp' | 'updateMandalaChart' | 'verifyEmail' | MutationKeySpecifier)[]; export type MutationFieldPolicy = { + createMandalaChart?: FieldPolicy | FieldReadFunction, deleteAccount?: FieldPolicy | FieldReadFunction, + deleteMandalaChart?: FieldPolicy | FieldReadFunction, findPassword?: FieldPolicy | FieldReadFunction, resetPassword?: FieldPolicy | FieldReadFunction, signIn?: FieldPolicy | FieldReadFunction, signOut?: FieldPolicy | FieldReadFunction, signUp?: FieldPolicy | FieldReadFunction, + updateMandalaChart?: FieldPolicy | FieldReadFunction, verifyEmail?: FieldPolicy | FieldReadFunction }; export type PurchasedInfoKeySpecifier = ('expiresAt' | 'isPurchased' | 'purchasedAt' | PurchasedInfoKeySpecifier)[]; @@ -31,10 +84,12 @@ export type PurchasedInfoFieldPolicy = { isPurchased?: FieldPolicy | FieldReadFunction, purchasedAt?: FieldPolicy | FieldReadFunction }; -export type QueryKeySpecifier = ('checkUser' | 'getUser' | 'recommendationForSubGoals' | QueryKeySpecifier)[]; +export type QueryKeySpecifier = ('checkUser' | 'getMandalaChart' | 'getUser' | 'getUserMandalaCharts' | 'recommendationForSubGoals' | QueryKeySpecifier)[]; export type QueryFieldPolicy = { checkUser?: FieldPolicy | FieldReadFunction, + getMandalaChart?: FieldPolicy | FieldReadFunction, getUser?: FieldPolicy | FieldReadFunction, + getUserMandalaCharts?: FieldPolicy | FieldReadFunction, recommendationForSubGoals?: FieldPolicy | FieldReadFunction }; export type RecommendationKeySpecifier = ('text' | RecommendationKeySpecifier)[]; @@ -80,6 +135,14 @@ export type TokenInfoFieldPolicy = { isVerified?: FieldPolicy | FieldReadFunction, token?: FieldPolicy | FieldReadFunction }; +export type UpdateMandalaChartFailureKeySpecifier = ('errorType' | UpdateMandalaChartFailureKeySpecifier)[]; +export type UpdateMandalaChartFailureFieldPolicy = { + errorType?: FieldPolicy | FieldReadFunction +}; +export type UpdateMandalaChartSuccessKeySpecifier = ('mandalaChart' | UpdateMandalaChartSuccessKeySpecifier)[]; +export type UpdateMandalaChartSuccessFieldPolicy = { + mandalaChart?: FieldPolicy | FieldReadFunction +}; export type UserKeySpecifier = ('_id' | 'createdAt' | 'email' | 'emailVerification' | 'nickname' | 'purchasedInfo' | 'resetPassword' | UserKeySpecifier)[]; export type UserFieldPolicy = { _id?: FieldPolicy | FieldReadFunction, @@ -99,6 +162,14 @@ export type VerifyEmailSuccessFieldPolicy = { success?: FieldPolicy | FieldReadFunction }; export type StrictTypedTypePolicies = { + CreateMandalaChartFailure?: Omit & { + keyFields?: false | CreateMandalaChartFailureKeySpecifier | (() => undefined | CreateMandalaChartFailureKeySpecifier), + fields?: CreateMandalaChartFailureFieldPolicy, + }, + CreateMandalaChartSuccess?: Omit & { + keyFields?: false | CreateMandalaChartSuccessKeySpecifier | (() => undefined | CreateMandalaChartSuccessKeySpecifier), + fields?: CreateMandalaChartSuccessFieldPolicy, + }, DeleteAccountFailure?: Omit & { keyFields?: false | DeleteAccountFailureKeySpecifier | (() => undefined | DeleteAccountFailureKeySpecifier), fields?: DeleteAccountFailureFieldPolicy, @@ -107,6 +178,14 @@ export type StrictTypedTypePolicies = { keyFields?: false | DeleteAccountSuccessKeySpecifier | (() => undefined | DeleteAccountSuccessKeySpecifier), fields?: DeleteAccountSuccessFieldPolicy, }, + DeleteMandalaChartFailure?: Omit & { + keyFields?: false | DeleteMandalaChartFailureKeySpecifier | (() => undefined | DeleteMandalaChartFailureKeySpecifier), + fields?: DeleteMandalaChartFailureFieldPolicy, + }, + DeleteMandalaChartSuccess?: Omit & { + keyFields?: false | DeleteMandalaChartSuccessKeySpecifier | (() => undefined | DeleteMandalaChartSuccessKeySpecifier), + fields?: DeleteMandalaChartSuccessFieldPolicy, + }, FindPasswordFailure?: Omit & { keyFields?: false | FindPasswordFailureKeySpecifier | (() => undefined | FindPasswordFailureKeySpecifier), fields?: FindPasswordFailureFieldPolicy, @@ -115,6 +194,30 @@ export type StrictTypedTypePolicies = { keyFields?: false | FindPasswordSuccessKeySpecifier | (() => undefined | FindPasswordSuccessKeySpecifier), fields?: FindPasswordSuccessFieldPolicy, }, + GetMandalaChartFailure?: Omit & { + keyFields?: false | GetMandalaChartFailureKeySpecifier | (() => undefined | GetMandalaChartFailureKeySpecifier), + fields?: GetMandalaChartFailureFieldPolicy, + }, + GetMandalaChartSuccess?: Omit & { + keyFields?: false | GetMandalaChartSuccessKeySpecifier | (() => undefined | GetMandalaChartSuccessKeySpecifier), + fields?: GetMandalaChartSuccessFieldPolicy, + }, + GetUserMandalaChartsFailure?: Omit & { + keyFields?: false | GetUserMandalaChartsFailureKeySpecifier | (() => undefined | GetUserMandalaChartsFailureKeySpecifier), + fields?: GetUserMandalaChartsFailureFieldPolicy, + }, + GetUserMandalaChartsSuccess?: Omit & { + keyFields?: false | GetUserMandalaChartsSuccessKeySpecifier | (() => undefined | GetUserMandalaChartsSuccessKeySpecifier), + fields?: GetUserMandalaChartsSuccessFieldPolicy, + }, + MandalaCell?: Omit & { + keyFields?: false | MandalaCellKeySpecifier | (() => undefined | MandalaCellKeySpecifier), + fields?: MandalaCellFieldPolicy, + }, + MandalaChart?: Omit & { + keyFields?: false | MandalaChartKeySpecifier | (() => undefined | MandalaChartKeySpecifier), + fields?: MandalaChartFieldPolicy, + }, Mutation?: Omit & { keyFields?: false | MutationKeySpecifier | (() => undefined | MutationKeySpecifier), fields?: MutationFieldPolicy, @@ -167,6 +270,14 @@ export type StrictTypedTypePolicies = { keyFields?: false | TokenInfoKeySpecifier | (() => undefined | TokenInfoKeySpecifier), fields?: TokenInfoFieldPolicy, }, + UpdateMandalaChartFailure?: Omit & { + keyFields?: false | UpdateMandalaChartFailureKeySpecifier | (() => undefined | UpdateMandalaChartFailureKeySpecifier), + fields?: UpdateMandalaChartFailureFieldPolicy, + }, + UpdateMandalaChartSuccess?: Omit & { + keyFields?: false | UpdateMandalaChartSuccessKeySpecifier | (() => undefined | UpdateMandalaChartSuccessKeySpecifier), + fields?: UpdateMandalaChartSuccessFieldPolicy, + }, User?: Omit & { keyFields?: false | UserKeySpecifier | (() => undefined | UserKeySpecifier), fields?: UserFieldPolicy, diff --git a/packages/web/package.json b/packages/web/package.json index 572bf34..128707d 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -4,16 +4,19 @@ "private": true, "scripts": { "dev": "concurrently \"next dev\" \"yarn gen --watch\"", - "build": "next build && next-sitemap", + "build": "yarn build:tailwind && next build && next-sitemap", + "build:tailwind": "tailwindcss -o public/styles.css", "start": "next start", "lint": "next lint", "gen": "graphql-codegen --config codegen.config.ts" }, "dependencies": { "@apollo/client": "^3.8.10", + "@heroicons/react": "^2.1.1", "@tailwindcss/line-clamp": "^0.4.4", "@vercel/analytics": "^1.1.1", "axios": "^1.6.5", + "file-saver": "^2.0.5", "html2canvas": "^1.4.1", "next": "13.5.4", "next-sitemap": "^4.2.3", @@ -34,6 +37,7 @@ "@graphql-codegen/typescript-react-apollo": "^4.1.0", "@parcel/watcher": "^2.4.0", "@tailwindcss/typography": "^0.5.10", + "@types/file-saver": "^2.0.7", "@types/node": "^20", "@types/react": "^18", "@types/react-dom": "^18", diff --git a/packages/web/public/images/example/en.png b/packages/web/public/images/example/en.png new file mode 100644 index 0000000..960a3ef Binary files /dev/null and b/packages/web/public/images/example/en.png differ diff --git a/packages/web/public/images/example/en_empty.png b/packages/web/public/images/example/en_empty.png new file mode 100644 index 0000000..3b923a4 Binary files /dev/null and b/packages/web/public/images/example/en_empty.png differ diff --git a/packages/web/public/images/example/ko.png b/packages/web/public/images/example/ko.png new file mode 100644 index 0000000..a091974 Binary files /dev/null and b/packages/web/public/images/example/ko.png differ diff --git a/packages/web/public/images/example/ko_empty.png b/packages/web/public/images/example/ko_empty.png new file mode 100644 index 0000000..80831d5 Binary files /dev/null and b/packages/web/public/images/example/ko_empty.png differ diff --git a/packages/web/public/images/example/zh.png b/packages/web/public/images/example/zh.png new file mode 100644 index 0000000..a68ddbc Binary files /dev/null and b/packages/web/public/images/example/zh.png differ diff --git a/packages/web/public/images/example/zh_empty.png b/packages/web/public/images/example/zh_empty.png new file mode 100644 index 0000000..2a4772d Binary files /dev/null and b/packages/web/public/images/example/zh_empty.png differ diff --git a/packages/web/public/sitemap-0.xml b/packages/web/public/sitemap-0.xml index 5ba9982..dd8fbc0 100644 --- a/packages/web/public/sitemap-0.xml +++ b/packages/web/public/sitemap-0.xml @@ -1,10 +1,14 @@ -https://www.lifeleader.me2024-01-15T16:01:39.729Zdaily1 -https://www.lifeleader.me/auth/find-password2024-01-15T16:01:39.729Zdaily1 -https://www.lifeleader.me/auth/reset-password2024-01-15T16:01:39.729Zdaily1 -https://www.lifeleader.me/auth/sign-in2024-01-15T16:01:39.729Zdaily1 -https://www.lifeleader.me/auth/sign-up2024-01-15T16:01:39.729Zdaily1 +https://www.lifeleader.me2024-01-28T14:28:30.312Zdaily1 +https://www.lifeleader.me/auth/delete-account2024-01-28T14:28:30.312Zdaily1 +https://www.lifeleader.me/auth/find-password2024-01-28T14:28:30.312Zdaily1 +https://www.lifeleader.me/auth/reset-password2024-01-28T14:28:30.312Zdaily1 +https://www.lifeleader.me/auth/sign-in2024-01-28T14:28:30.312Zdaily1 +https://www.lifeleader.me/auth/sign-up2024-01-28T14:28:30.312Zdaily1 +https://www.lifeleader.me/auth/verify-email2024-01-28T14:28:30.312Zdaily1 +https://www.lifeleader.me/mandala/chart2024-01-28T14:28:30.312Zdaily1 +https://www.lifeleader.me/mandala/my-list2024-01-28T14:28:30.312Zdaily1 https://www.lifeleader.me/en0.7 https://www.lifeleader.me/ko0.7 https://www.lifeleader.me/zh-Hant0.7 diff --git a/packages/web/public/styles.css b/packages/web/public/styles.css new file mode 100644 index 0000000..97a0127 --- /dev/null +++ b/packages/web/public/styles.css @@ -0,0 +1,1743 @@ +/* +! tailwindcss v3.4.0 | MIT License | https://tailwindcss.com +*/ + +/* +1. Prevent padding and border from affecting element width. (https://github.com/mozdevs/cssremedy/issues/4) +2. Allow adding a border to an element by just adding a border-width. (https://github.com/tailwindcss/tailwindcss/pull/116) +*/ + +*, +::before, +::after { + box-sizing: border-box; + /* 1 */ + border-width: 0; + /* 2 */ + border-style: solid; + /* 2 */ + border-color: #e5e7eb; + /* 2 */ +} + +::before, +::after { + --tw-content: ''; +} + +/* +1. Use a consistent sensible line-height in all browsers. +2. Prevent adjustments of font size after orientation changes in iOS. +3. Use a more readable tab size. +4. Use the user's configured `sans` font-family by default. +5. Use the user's configured `sans` font-feature-settings by default. +6. Use the user's configured `sans` font-variation-settings by default. +7. Disable tap highlights on iOS +*/ + +html, +:host { + line-height: 1.5; + /* 1 */ + -webkit-text-size-adjust: 100%; + /* 2 */ + -moz-tab-size: 4; + /* 3 */ + tab-size: 4; + /* 3 */ + font-family: ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; + /* 4 */ + font-feature-settings: normal; + /* 5 */ + font-variation-settings: normal; + /* 6 */ + -webkit-tap-highlight-color: transparent; + /* 7 */ +} + +/* +1. Remove the margin in all browsers. +2. Inherit line-height from `html` so users can set them as a class directly on the `html` element. +*/ + +body { + margin: 0; + /* 1 */ + line-height: inherit; + /* 2 */ +} + +/* +1. Add the correct height in Firefox. +2. Correct the inheritance of border color in Firefox. (https://bugzilla.mozilla.org/show_bug.cgi?id=190655) +3. Ensure horizontal rules are visible by default. +*/ + +hr { + height: 0; + /* 1 */ + color: inherit; + /* 2 */ + border-top-width: 1px; + /* 3 */ +} + +/* +Add the correct text decoration in Chrome, Edge, and Safari. +*/ + +abbr:where([title]) { + -webkit-text-decoration: underline dotted; + text-decoration: underline dotted; +} + +/* +Remove the default font size and weight for headings. +*/ + +h1, +h2, +h3, +h4, +h5, +h6 { + font-size: inherit; + font-weight: inherit; +} + +/* +Reset links to optimize for opt-in styling instead of opt-out. +*/ + +a { + color: inherit; + text-decoration: inherit; +} + +/* +Add the correct font weight in Edge and Safari. +*/ + +b, +strong { + font-weight: bolder; +} + +/* +1. Use the user's configured `mono` font-family by default. +2. Use the user's configured `mono` font-feature-settings by default. +3. Use the user's configured `mono` font-variation-settings by default. +4. Correct the odd `em` font sizing in all browsers. +*/ + +code, +kbd, +samp, +pre { + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; + /* 1 */ + font-feature-settings: normal; + /* 2 */ + font-variation-settings: normal; + /* 3 */ + font-size: 1em; + /* 4 */ +} + +/* +Add the correct font size in all browsers. +*/ + +small { + font-size: 80%; +} + +/* +Prevent `sub` and `sup` elements from affecting the line height in all browsers. +*/ + +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} + +sub { + bottom: -0.25em; +} + +sup { + top: -0.5em; +} + +/* +1. Remove text indentation from table contents in Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=999088, https://bugs.webkit.org/show_bug.cgi?id=201297) +2. Correct table border color inheritance in all Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=935729, https://bugs.webkit.org/show_bug.cgi?id=195016) +3. Remove gaps between table borders by default. +*/ + +table { + text-indent: 0; + /* 1 */ + border-color: inherit; + /* 2 */ + border-collapse: collapse; + /* 3 */ +} + +/* +1. Change the font styles in all browsers. +2. Remove the margin in Firefox and Safari. +3. Remove default padding in all browsers. +*/ + +button, +input, +optgroup, +select, +textarea { + font-family: inherit; + /* 1 */ + font-feature-settings: inherit; + /* 1 */ + font-variation-settings: inherit; + /* 1 */ + font-size: 100%; + /* 1 */ + font-weight: inherit; + /* 1 */ + line-height: inherit; + /* 1 */ + color: inherit; + /* 1 */ + margin: 0; + /* 2 */ + padding: 0; + /* 3 */ +} + +/* +Remove the inheritance of text transform in Edge and Firefox. +*/ + +button, +select { + text-transform: none; +} + +/* +1. Correct the inability to style clickable types in iOS and Safari. +2. Remove default button styles. +*/ + +button, +[type='button'], +[type='reset'], +[type='submit'] { + -webkit-appearance: button; + /* 1 */ + background-color: transparent; + /* 2 */ + background-image: none; + /* 2 */ +} + +/* +Use the modern Firefox focus style for all focusable elements. +*/ + +:-moz-focusring { + outline: auto; +} + +/* +Remove the additional `:invalid` styles in Firefox. (https://github.com/mozilla/gecko-dev/blob/2f9eacd9d3d995c937b4251a5557d95d494c9be1/layout/style/res/forms.css#L728-L737) +*/ + +:-moz-ui-invalid { + box-shadow: none; +} + +/* +Add the correct vertical alignment in Chrome and Firefox. +*/ + +progress { + vertical-align: baseline; +} + +/* +Correct the cursor style of increment and decrement buttons in Safari. +*/ + +::-webkit-inner-spin-button, +::-webkit-outer-spin-button { + height: auto; +} + +/* +1. Correct the odd appearance in Chrome and Safari. +2. Correct the outline style in Safari. +*/ + +[type='search'] { + -webkit-appearance: textfield; + /* 1 */ + outline-offset: -2px; + /* 2 */ +} + +/* +Remove the inner padding in Chrome and Safari on macOS. +*/ + +::-webkit-search-decoration { + -webkit-appearance: none; +} + +/* +1. Correct the inability to style clickable types in iOS and Safari. +2. Change font properties to `inherit` in Safari. +*/ + +::-webkit-file-upload-button { + -webkit-appearance: button; + /* 1 */ + font: inherit; + /* 2 */ +} + +/* +Add the correct display in Chrome and Safari. +*/ + +summary { + display: list-item; +} + +/* +Removes the default spacing and border for appropriate elements. +*/ + +blockquote, +dl, +dd, +h1, +h2, +h3, +h4, +h5, +h6, +hr, +figure, +p, +pre { + margin: 0; +} + +fieldset { + margin: 0; + padding: 0; +} + +legend { + padding: 0; +} + +ol, +ul, +menu { + list-style: none; + margin: 0; + padding: 0; +} + +/* +Reset default styling for dialogs. +*/ + +dialog { + padding: 0; +} + +/* +Prevent resizing textareas horizontally by default. +*/ + +textarea { + resize: vertical; +} + +/* +1. Reset the default placeholder opacity in Firefox. (https://github.com/tailwindlabs/tailwindcss/issues/3300) +2. Set the default placeholder color to the user's configured gray 400 color. +*/ + +input::placeholder, +textarea::placeholder { + opacity: 1; + /* 1 */ + color: #9ca3af; + /* 2 */ +} + +/* +Set the default cursor for buttons. +*/ + +button, +[role="button"] { + cursor: pointer; +} + +/* +Make sure disabled buttons don't get the pointer cursor. +*/ + +:disabled { + cursor: default; +} + +/* +1. Make replaced elements `display: block` by default. (https://github.com/mozdevs/cssremedy/issues/14) +2. Add `vertical-align: middle` to align replaced elements more sensibly by default. (https://github.com/jensimmons/cssremedy/issues/14#issuecomment-634934210) + This can trigger a poorly considered lint error in some tools but is included by design. +*/ + +img, +svg, +video, +canvas, +audio, +iframe, +embed, +object { + display: block; + /* 1 */ + vertical-align: middle; + /* 2 */ +} + +/* +Constrain images and videos to the parent width and preserve their intrinsic aspect ratio. (https://github.com/mozdevs/cssremedy/issues/14) +*/ + +img, +video { + max-width: 100%; + height: auto; +} + +/* Make elements with the HTML hidden attribute stay hidden by default */ + +[hidden] { + display: none; +} + +*, ::before, ::after { + --tw-border-spacing-x: 0; + --tw-border-spacing-y: 0; + --tw-translate-x: 0; + --tw-translate-y: 0; + --tw-rotate: 0; + --tw-skew-x: 0; + --tw-skew-y: 0; + --tw-scale-x: 1; + --tw-scale-y: 1; + --tw-pan-x: ; + --tw-pan-y: ; + --tw-pinch-zoom: ; + --tw-scroll-snap-strictness: proximity; + --tw-gradient-from-position: ; + --tw-gradient-via-position: ; + --tw-gradient-to-position: ; + --tw-ordinal: ; + --tw-slashed-zero: ; + --tw-numeric-figure: ; + --tw-numeric-spacing: ; + --tw-numeric-fraction: ; + --tw-ring-inset: ; + --tw-ring-offset-width: 0px; + --tw-ring-offset-color: #fff; + --tw-ring-color: rgb(59 130 246 / 0.5); + --tw-ring-offset-shadow: 0 0 #0000; + --tw-ring-shadow: 0 0 #0000; + --tw-shadow: 0 0 #0000; + --tw-shadow-colored: 0 0 #0000; + --tw-blur: ; + --tw-brightness: ; + --tw-contrast: ; + --tw-grayscale: ; + --tw-hue-rotate: ; + --tw-invert: ; + --tw-saturate: ; + --tw-sepia: ; + --tw-drop-shadow: ; + --tw-backdrop-blur: ; + --tw-backdrop-brightness: ; + --tw-backdrop-contrast: ; + --tw-backdrop-grayscale: ; + --tw-backdrop-hue-rotate: ; + --tw-backdrop-invert: ; + --tw-backdrop-opacity: ; + --tw-backdrop-saturate: ; + --tw-backdrop-sepia: ; +} + +::backdrop { + --tw-border-spacing-x: 0; + --tw-border-spacing-y: 0; + --tw-translate-x: 0; + --tw-translate-y: 0; + --tw-rotate: 0; + --tw-skew-x: 0; + --tw-skew-y: 0; + --tw-scale-x: 1; + --tw-scale-y: 1; + --tw-pan-x: ; + --tw-pan-y: ; + --tw-pinch-zoom: ; + --tw-scroll-snap-strictness: proximity; + --tw-gradient-from-position: ; + --tw-gradient-via-position: ; + --tw-gradient-to-position: ; + --tw-ordinal: ; + --tw-slashed-zero: ; + --tw-numeric-figure: ; + --tw-numeric-spacing: ; + --tw-numeric-fraction: ; + --tw-ring-inset: ; + --tw-ring-offset-width: 0px; + --tw-ring-offset-color: #fff; + --tw-ring-color: rgb(59 130 246 / 0.5); + --tw-ring-offset-shadow: 0 0 #0000; + --tw-ring-shadow: 0 0 #0000; + --tw-shadow: 0 0 #0000; + --tw-shadow-colored: 0 0 #0000; + --tw-blur: ; + --tw-brightness: ; + --tw-contrast: ; + --tw-grayscale: ; + --tw-hue-rotate: ; + --tw-invert: ; + --tw-saturate: ; + --tw-sepia: ; + --tw-drop-shadow: ; + --tw-backdrop-blur: ; + --tw-backdrop-brightness: ; + --tw-backdrop-contrast: ; + --tw-backdrop-grayscale: ; + --tw-backdrop-hue-rotate: ; + --tw-backdrop-invert: ; + --tw-backdrop-opacity: ; + --tw-backdrop-saturate: ; + --tw-backdrop-sepia: ; +} + +.container { + width: 100%; +} + +@media (min-width: 640px) { + .container { + max-width: 640px; + } +} + +@media (min-width: 768px) { + .container { + max-width: 768px; + } +} + +@media (min-width: 1024px) { + .container { + max-width: 1024px; + } +} + +@media (min-width: 1280px) { + .container { + max-width: 1280px; + } +} + +@media (min-width: 1536px) { + .container { + max-width: 1536px; + } +} + +.sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border-width: 0; +} + +.pointer-events-none { + pointer-events: none; +} + +.fixed { + position: fixed; +} + +.absolute { + position: absolute; +} + +.relative { + position: relative; +} + +.inset-0 { + inset: 0px; +} + +.bottom-10 { + bottom: 2.5rem; +} + +.left-1 { + left: 0.25rem; +} + +.left-1\/2 { + left: 50%; +} + +.left-10 { + left: 2.5rem; +} + +.left-\[9999px\] { + left: 9999px; +} + +.right-10 { + right: 2.5rem; +} + +.top-1 { + top: 0.25rem; +} + +.top-1\/2 { + top: 50%; +} + +.z-40 { + z-index: 40; +} + +.z-50 { + z-index: 50; +} + +.m-2 { + margin: 0.5rem; +} + +.m-4 { + margin: 1rem; +} + +.mx-auto { + margin-left: auto; + margin-right: auto; +} + +.my-2 { + margin-top: 0.5rem; + margin-bottom: 0.5rem; +} + +.my-4 { + margin-top: 1rem; + margin-bottom: 1rem; +} + +.my-8 { + margin-top: 2rem; + margin-bottom: 2rem; +} + +.mb-1 { + margin-bottom: 0.25rem; +} + +.mb-2 { + margin-bottom: 0.5rem; +} + +.mb-4 { + margin-bottom: 1rem; +} + +.mb-6 { + margin-bottom: 1.5rem; +} + +.mb-8 { + margin-bottom: 2rem; +} + +.ml-1 { + margin-left: 0.25rem; +} + +.ml-2 { + margin-left: 0.5rem; +} + +.mr-1 { + margin-right: 0.25rem; +} + +.mr-2 { + margin-right: 0.5rem; +} + +.mr-4 { + margin-right: 1rem; +} + +.mt-1 { + margin-top: 0.25rem; +} + +.mt-10 { + margin-top: 2.5rem; +} + +.mt-2 { + margin-top: 0.5rem; +} + +.mt-4 { + margin-top: 1rem; +} + +.line-clamp-1 { + overflow: hidden; + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 1; +} + +.line-clamp-none { + overflow: visible; + display: block; + -webkit-box-orient: horizontal; + -webkit-line-clamp: none; +} + +.block { + display: block; +} + +.inline-block { + display: inline-block; +} + +.flex { + display: flex; +} + +.inline-flex { + display: inline-flex; +} + +.grid { + display: grid; +} + +.hidden { + display: none; +} + +.size-20 { + width: 5rem; + height: 5rem; +} + +.size-24 { + width: 6rem; + height: 6rem; +} + +.h-2 { + height: 0.5rem; +} + +.h-36 { + height: 9rem; +} + +.h-4 { + height: 1rem; +} + +.h-5 { + height: 1.25rem; +} + +.h-6 { + height: 1.5rem; +} + +.h-8 { + height: 2rem; +} + +.h-max { + height: max-content; +} + +.h-px { + height: 1px; +} + +.h-screen { + height: 100vh; +} + +.max-h-\[400px\] { + max-height: 400px; +} + +.min-h-screen { + min-height: 100vh; +} + +.w-11\/12 { + width: 91.666667%; +} + +.w-14 { + width: 3.5rem; +} + +.w-2 { + width: 0.5rem; +} + +.w-36 { + width: 9rem; +} + +.w-4 { + width: 1rem; +} + +.w-4\/5 { + width: 80%; +} + +.w-48 { + width: 12rem; +} + +.w-5 { + width: 1.25rem; +} + +.w-6 { + width: 1.5rem; +} + +.w-96 { + width: 24rem; +} + +.w-\[720px\] { + width: 720px; +} + +.w-full { + width: 100%; +} + +.w-max { + width: max-content; +} + +.min-w-60 { + min-width: 15rem; +} + +.max-w-4xl { + max-width: 56rem; +} + +.max-w-\[400px\] { + max-width: 400px; +} + +.max-w-\[720px\] { + max-width: 720px; +} + +.max-w-fit { + max-width: -moz-fit-content; + max-width: fit-content; +} + +.max-w-xs { + max-width: 20rem; +} + +.flex-grow { + flex-grow: 1; +} + +.-translate-x-1\/2 { + --tw-translate-x: -50%; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} + +.-translate-x-full { + --tw-translate-x: -100%; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} + +.-translate-y-1\/2 { + --tw-translate-y: -50%; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} + +.translate-x-0 { + --tw-translate-x: 0px; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} + +.translate-x-6 { + --tw-translate-x: 1.5rem; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} + +.transform { + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} + +.cursor-pointer { + cursor: pointer; +} + +.cursor-text { + cursor: text; +} + +.grid-cols-2 { + grid-template-columns: repeat(2, minmax(0, 1fr)); +} + +.grid-cols-3 { + grid-template-columns: repeat(3, minmax(0, 1fr)); +} + +.flex-col { + flex-direction: column; +} + +.items-center { + align-items: center; +} + +.items-baseline { + align-items: baseline; +} + +.justify-end { + justify-content: flex-end; +} + +.justify-center { + justify-content: center; +} + +.justify-between { + justify-content: space-between; +} + +.gap-2 { + gap: 0.5rem; +} + +.gap-4 { + gap: 1rem; +} + +.space-x-2 > :not([hidden]) ~ :not([hidden]) { + --tw-space-x-reverse: 0; + margin-right: calc(0.5rem * var(--tw-space-x-reverse)); + margin-left: calc(0.5rem * calc(1 - var(--tw-space-x-reverse))); +} + +.overflow-auto { + overflow: auto; +} + +.overflow-hidden { + overflow: hidden; +} + +.whitespace-normal { + white-space: normal; +} + +.break-words { + overflow-wrap: break-word; +} + +.break-all { + word-break: break-all; +} + +.rounded { + border-radius: 0.25rem; +} + +.rounded-full { + border-radius: 9999px; +} + +.rounded-lg { + border-radius: 0.5rem; +} + +.rounded-md { + border-radius: 0.375rem; +} + +.border { + border-width: 1px; +} + +.border-0 { + border-width: 0px; +} + +.border-2 { + border-width: 2px; +} + +.border-4 { + border-width: 4px; +} + +.border-l { + border-left-width: 1px; +} + +.border-black { + --tw-border-opacity: 1; + border-color: rgb(0 0 0 / var(--tw-border-opacity)); +} + +.border-blue-200 { + --tw-border-opacity: 1; + border-color: rgb(191 219 254 / var(--tw-border-opacity)); +} + +.border-blue-400 { + --tw-border-opacity: 1; + border-color: rgb(96 165 250 / var(--tw-border-opacity)); +} + +.border-cyan-200 { + --tw-border-opacity: 1; + border-color: rgb(165 243 252 / var(--tw-border-opacity)); +} + +.border-cyan-300 { + --tw-border-opacity: 1; + border-color: rgb(103 232 249 / var(--tw-border-opacity)); +} + +.border-gray-200 { + --tw-border-opacity: 1; + border-color: rgb(229 231 235 / var(--tw-border-opacity)); +} + +.border-gray-300 { + --tw-border-opacity: 1; + border-color: rgb(209 213 219 / var(--tw-border-opacity)); +} + +.border-gray-700 { + --tw-border-opacity: 1; + border-color: rgb(55 65 81 / var(--tw-border-opacity)); +} + +.border-green-200 { + --tw-border-opacity: 1; + border-color: rgb(187 247 208 / var(--tw-border-opacity)); +} + +.border-green-300 { + --tw-border-opacity: 1; + border-color: rgb(134 239 172 / var(--tw-border-opacity)); +} + +.border-indigo-600 { + --tw-border-opacity: 1; + border-color: rgb(79 70 229 / var(--tw-border-opacity)); +} + +.border-indigo-700 { + --tw-border-opacity: 1; + border-color: rgb(67 56 202 / var(--tw-border-opacity)); +} + +.border-pink-200 { + --tw-border-opacity: 1; + border-color: rgb(251 207 232 / var(--tw-border-opacity)); +} + +.border-red-200 { + --tw-border-opacity: 1; + border-color: rgb(254 202 202 / var(--tw-border-opacity)); +} + +.border-red-300 { + --tw-border-opacity: 1; + border-color: rgb(252 165 165 / var(--tw-border-opacity)); +} + +.border-red-500 { + --tw-border-opacity: 1; + border-color: rgb(239 68 68 / var(--tw-border-opacity)); +} + +.border-slate-400 { + --tw-border-opacity: 1; + border-color: rgb(148 163 184 / var(--tw-border-opacity)); +} + +.border-yellow-200 { + --tw-border-opacity: 1; + border-color: rgb(254 240 138 / var(--tw-border-opacity)); +} + +.bg-black { + --tw-bg-opacity: 1; + background-color: rgb(0 0 0 / var(--tw-bg-opacity)); +} + +.bg-blue-100 { + --tw-bg-opacity: 1; + background-color: rgb(219 234 254 / var(--tw-bg-opacity)); +} + +.bg-blue-500 { + --tw-bg-opacity: 1; + background-color: rgb(59 130 246 / var(--tw-bg-opacity)); +} + +.bg-blue-600 { + --tw-bg-opacity: 1; + background-color: rgb(37 99 235 / var(--tw-bg-opacity)); +} + +.bg-cyan-100 { + --tw-bg-opacity: 1; + background-color: rgb(207 250 254 / var(--tw-bg-opacity)); +} + +.bg-emerald-500 { + --tw-bg-opacity: 1; + background-color: rgb(16 185 129 / var(--tw-bg-opacity)); +} + +.bg-gray-100 { + --tw-bg-opacity: 1; + background-color: rgb(243 244 246 / var(--tw-bg-opacity)); +} + +.bg-gray-200 { + --tw-bg-opacity: 1; + background-color: rgb(229 231 235 / var(--tw-bg-opacity)); +} + +.bg-gray-400 { + --tw-bg-opacity: 1; + background-color: rgb(156 163 175 / var(--tw-bg-opacity)); +} + +.bg-gray-600 { + --tw-bg-opacity: 1; + background-color: rgb(75 85 99 / var(--tw-bg-opacity)); +} + +.bg-gray-800 { + --tw-bg-opacity: 1; + background-color: rgb(31 41 55 / var(--tw-bg-opacity)); +} + +.bg-green-100 { + --tw-bg-opacity: 1; + background-color: rgb(220 252 231 / var(--tw-bg-opacity)); +} + +.bg-green-500 { + --tw-bg-opacity: 1; + background-color: rgb(34 197 94 / var(--tw-bg-opacity)); +} + +.bg-indigo-500 { + --tw-bg-opacity: 1; + background-color: rgb(99 102 241 / var(--tw-bg-opacity)); +} + +.bg-orange-100 { + --tw-bg-opacity: 1; + background-color: rgb(255 237 213 / var(--tw-bg-opacity)); +} + +.bg-red-500 { + --tw-bg-opacity: 1; + background-color: rgb(239 68 68 / var(--tw-bg-opacity)); +} + +.bg-white { + --tw-bg-opacity: 1; + background-color: rgb(255 255 255 / var(--tw-bg-opacity)); +} + +.bg-yellow-100 { + --tw-bg-opacity: 1; + background-color: rgb(254 249 195 / var(--tw-bg-opacity)); +} + +.bg-opacity-25 { + --tw-bg-opacity: 0.25; +} + +.bg-opacity-50 { + --tw-bg-opacity: 0.5; +} + +.p-0 { + padding: 0px; +} + +.p-1 { + padding: 0.25rem; +} + +.p-2 { + padding: 0.5rem; +} + +.p-4 { + padding: 1rem; +} + +.p-40 { + padding: 10rem; +} + +.p-6 { + padding: 1.5rem; +} + +.px-2 { + padding-left: 0.5rem; + padding-right: 0.5rem; +} + +.px-3 { + padding-left: 0.75rem; + padding-right: 0.75rem; +} + +.px-4 { + padding-left: 1rem; + padding-right: 1rem; +} + +.py-1 { + padding-top: 0.25rem; + padding-bottom: 0.25rem; +} + +.py-2 { + padding-top: 0.5rem; + padding-bottom: 0.5rem; +} + +.py-4 { + padding-top: 1rem; + padding-bottom: 1rem; +} + +.pb-2 { + padding-bottom: 0.5rem; +} + +.pl-2 { + padding-left: 0.5rem; +} + +.pr-2 { + padding-right: 0.5rem; +} + +.pt-2 { + padding-top: 0.5rem; +} + +.pt-4 { + padding-top: 1rem; +} + +.text-left { + text-align: left; +} + +.text-center { + text-align: center; +} + +.text-2xl { + font-size: 1.5rem; + line-height: 2rem; +} + +.text-3xl { + font-size: 1.875rem; + line-height: 2.25rem; +} + +.text-lg { + font-size: 1.125rem; + line-height: 1.75rem; +} + +.text-sm { + font-size: 0.875rem; + line-height: 1.25rem; +} + +.text-xl { + font-size: 1.25rem; + line-height: 1.75rem; +} + +.text-xs { + font-size: 0.75rem; + line-height: 1rem; +} + +.font-bold { + font-weight: 700; +} + +.font-medium { + font-weight: 500; +} + +.font-semibold { + font-weight: 600; +} + +.uppercase { + text-transform: uppercase; +} + +.lowercase { + text-transform: lowercase; +} + +.italic { + font-style: italic; +} + +.tracking-\[-1px\] { + letter-spacing: -1px; +} + +.text-black { + --tw-text-opacity: 1; + color: rgb(0 0 0 / var(--tw-text-opacity)); +} + +.text-blue-300 { + --tw-text-opacity: 1; + color: rgb(147 197 253 / var(--tw-text-opacity)); +} + +.text-blue-500 { + --tw-text-opacity: 1; + color: rgb(59 130 246 / var(--tw-text-opacity)); +} + +.text-blue-600 { + --tw-text-opacity: 1; + color: rgb(37 99 235 / var(--tw-text-opacity)); +} + +.text-blue-800 { + --tw-text-opacity: 1; + color: rgb(30 64 175 / var(--tw-text-opacity)); +} + +.text-cyan-300 { + --tw-text-opacity: 1; + color: rgb(103 232 249 / var(--tw-text-opacity)); +} + +.text-cyan-800 { + --tw-text-opacity: 1; + color: rgb(21 94 117 / var(--tw-text-opacity)); +} + +.text-cyan-900 { + --tw-text-opacity: 1; + color: rgb(22 78 99 / var(--tw-text-opacity)); +} + +.text-gray-300 { + --tw-text-opacity: 1; + color: rgb(209 213 219 / var(--tw-text-opacity)); +} + +.text-gray-400 { + --tw-text-opacity: 1; + color: rgb(156 163 175 / var(--tw-text-opacity)); +} + +.text-gray-500 { + --tw-text-opacity: 1; + color: rgb(107 114 128 / var(--tw-text-opacity)); +} + +.text-gray-600 { + --tw-text-opacity: 1; + color: rgb(75 85 99 / var(--tw-text-opacity)); +} + +.text-gray-700 { + --tw-text-opacity: 1; + color: rgb(55 65 81 / var(--tw-text-opacity)); +} + +.text-gray-800 { + --tw-text-opacity: 1; + color: rgb(31 41 55 / var(--tw-text-opacity)); +} + +.text-gray-900 { + --tw-text-opacity: 1; + color: rgb(17 24 39 / var(--tw-text-opacity)); +} + +.text-green-300 { + --tw-text-opacity: 1; + color: rgb(134 239 172 / var(--tw-text-opacity)); +} + +.text-green-800 { + --tw-text-opacity: 1; + color: rgb(22 101 52 / var(--tw-text-opacity)); +} + +.text-indigo-200 { + --tw-text-opacity: 1; + color: rgb(199 210 254 / var(--tw-text-opacity)); +} + +.text-purple-200 { + --tw-text-opacity: 1; + color: rgb(233 213 255 / var(--tw-text-opacity)); +} + +.text-purple-300 { + --tw-text-opacity: 1; + color: rgb(216 180 254 / var(--tw-text-opacity)); +} + +.text-red-500 { + --tw-text-opacity: 1; + color: rgb(239 68 68 / var(--tw-text-opacity)); +} + +.text-red-800 { + --tw-text-opacity: 1; + color: rgb(153 27 27 / var(--tw-text-opacity)); +} + +.text-red-900 { + --tw-text-opacity: 1; + color: rgb(127 29 29 / var(--tw-text-opacity)); +} + +.text-slate-100 { + --tw-text-opacity: 1; + color: rgb(241 245 249 / var(--tw-text-opacity)); +} + +.text-white { + --tw-text-opacity: 1; + color: rgb(255 255 255 / var(--tw-text-opacity)); +} + +.text-opacity-25 { + --tw-text-opacity: 0.25; +} + +.text-opacity-50 { + --tw-text-opacity: 0.5; +} + +.opacity-0 { + opacity: 0; +} + +.opacity-100 { + opacity: 1; +} + +.shadow-lg { + --tw-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1); + --tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); +} + +.shadow-sm { + --tw-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05); + --tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); +} + +.filter { + filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow); +} + +.transition { + transition-property: color, background-color, border-color, fill, stroke, opacity, box-shadow, transform, filter, -webkit-text-decoration-color, -webkit-backdrop-filter; + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter; + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter, -webkit-text-decoration-color, -webkit-backdrop-filter; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; +} + +.transition-all { + transition-property: all; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; +} + +.transition-opacity { + transition-property: opacity; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; +} + +.transition-transform { + transition-property: transform; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; +} + +.duration-300 { + transition-duration: 300ms; +} + +.line-clamp-1 { + overflow: hidden; + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 1; +} + +.line-clamp-none { + -webkit-line-clamp: unset; +} + +.first\:rounded-l-lg:first-child { + border-top-left-radius: 0.5rem; + border-bottom-left-radius: 0.5rem; +} + +.last\:rounded-r-lg:last-child { + border-top-right-radius: 0.5rem; + border-bottom-right-radius: 0.5rem; +} + +.visited\:text-purple-800:visited { + color: rgb(107 33 168 ); +} + +.hover\:-translate-y-1:hover { + --tw-translate-y: -0.25rem; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} + +.hover\:cursor-pointer:hover { + cursor: pointer; +} + +.hover\:bg-blue-600:hover { + --tw-bg-opacity: 1; + background-color: rgb(37 99 235 / var(--tw-bg-opacity)); +} + +.hover\:bg-blue-700:hover { + --tw-bg-opacity: 1; + background-color: rgb(29 78 216 / var(--tw-bg-opacity)); +} + +.hover\:bg-emerald-400:hover { + --tw-bg-opacity: 1; + background-color: rgb(52 211 153 / var(--tw-bg-opacity)); +} + +.hover\:bg-gray-100:hover { + --tw-bg-opacity: 1; + background-color: rgb(243 244 246 / var(--tw-bg-opacity)); +} + +.hover\:bg-gray-200:hover { + --tw-bg-opacity: 1; + background-color: rgb(229 231 235 / var(--tw-bg-opacity)); +} + +.hover\:bg-green-700:hover { + --tw-bg-opacity: 1; + background-color: rgb(21 128 61 / var(--tw-bg-opacity)); +} + +.hover\:bg-indigo-400:hover { + --tw-bg-opacity: 1; + background-color: rgb(129 140 248 / var(--tw-bg-opacity)); +} + +.hover\:bg-red-700:hover { + --tw-bg-opacity: 1; + background-color: rgb(185 28 28 / var(--tw-bg-opacity)); +} + +.hover\:text-blue-600:hover { + --tw-text-opacity: 1; + color: rgb(37 99 235 / var(--tw-text-opacity)); +} + +.hover\:text-blue-700:hover { + --tw-text-opacity: 1; + color: rgb(29 78 216 / var(--tw-text-opacity)); +} + +.hover\:text-blue-800:hover { + --tw-text-opacity: 1; + color: rgb(30 64 175 / var(--tw-text-opacity)); +} + +.hover\:text-gray-800:hover { + --tw-text-opacity: 1; + color: rgb(31 41 55 / var(--tw-text-opacity)); +} + +.hover\:text-red-500:hover { + --tw-text-opacity: 1; + color: rgb(239 68 68 / var(--tw-text-opacity)); +} + +.hover\:underline:hover { + -webkit-text-decoration-line: underline; + text-decoration-line: underline; +} + +.hover\:shadow-2xl:hover { + --tw-shadow: 0 25px 50px -12px rgb(0 0 0 / 0.25); + --tw-shadow-colored: 0 25px 50px -12px var(--tw-shadow-color); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); +} + +.focus\:border-blue-500:focus { + --tw-border-opacity: 1; + border-color: rgb(59 130 246 / var(--tw-border-opacity)); +} + +.focus\:outline-none:focus { + outline: 2px solid transparent; + outline-offset: 2px; +} + +.focus\:ring-1:focus { + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); +} + +.focus\:ring-2:focus { + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); +} + +.focus\:ring-blue-500:focus { + --tw-ring-opacity: 1; + --tw-ring-color: rgb(59 130 246 / var(--tw-ring-opacity)); +} + +.focus\:ring-emerald-500:focus { + --tw-ring-opacity: 1; + --tw-ring-color: rgb(16 185 129 / var(--tw-ring-opacity)); +} + +.focus\:ring-indigo-500:focus { + --tw-ring-opacity: 1; + --tw-ring-color: rgb(99 102 241 / var(--tw-ring-opacity)); +} + +.focus\:ring-opacity-50:focus { + --tw-ring-opacity: 0.5; +} + +@media (min-width: 640px) { + .sm\:text-xs { + font-size: 0.75rem; + line-height: 1rem; + } +} + +@media (min-width: 768px) { + .md\:flex { + display: flex; + } + + .md\:hidden { + display: none; + } + + .md\:grid-cols-4 { + grid-template-columns: repeat(4, minmax(0, 1fr)); + } + + .md\:px-10 { + padding-left: 2.5rem; + padding-right: 2.5rem; + } + + .md\:text-base { + font-size: 1rem; + line-height: 1.5rem; + } +} + +@media (prefers-color-scheme: dark) { + .dark\:bg-gray-700 { + --tw-bg-opacity: 1; + background-color: rgb(55 65 81 / var(--tw-bg-opacity)); + } +} \ No newline at end of file diff --git a/packages/web/src/components/Header/Header.tsx b/packages/web/src/components/Header/Header.tsx deleted file mode 100644 index 95fc23a..0000000 --- a/packages/web/src/components/Header/Header.tsx +++ /dev/null @@ -1,87 +0,0 @@ -import { LANGUAGE_NAMES_SHORT, SUPPORTING_LANGUAGES } from '@/constants/i18n' -import { useUserContext } from '@/contexts/UserContext' - -import useGoTo from '@/hooks/useGoTo' -import useI18n from '@/hooks/useI18n' -import useSignOut from '@/hooks/useSignOut/useSignOut' -import TRANSLATIONS from '@/pages/auth/auth.i18n' -import Link from 'next/link' -import { useRouter } from 'next/router' -import LifeLeaderIcon from 'public/logo/image-only.png' - -const Header = () => { - const { locale, locales } = useRouter() - const { changeLanguage, getTranslation } = useI18n() - const signOut = useSignOut() - const translation = getTranslation(TRANSLATIONS) - const { - user: { nickname }, - isSignedIn, - } = useUserContext() - const handleSignOut = async () => { - await signOut() - } - const { goTo } = useGoTo() - - return ( -
-
goTo('/')} - style={{ cursor: 'pointer' }} - > - Logo - - Life Leader - -
- - {/* Language Selector and Authentication */} -
- {/* Language Selector */} - - - {/* Authentication Section */} - {isSignedIn ? ( -
- - {nickname} - - -
- ) : ( - - {translation('signIn')} - - )} -
-
- ) -} - -export default Header diff --git a/packages/web/src/components/Footer/Footer.tsx b/packages/web/src/components/Layout/Footer.tsx similarity index 100% rename from packages/web/src/components/Footer/Footer.tsx rename to packages/web/src/components/Layout/Footer.tsx diff --git a/packages/web/src/components/Layout/Header.tsx b/packages/web/src/components/Layout/Header.tsx new file mode 100644 index 0000000..35e6f29 --- /dev/null +++ b/packages/web/src/components/Layout/Header.tsx @@ -0,0 +1,129 @@ +import { + COMMON_TRANSLATIONS, + LANGUAGE_NAMES_SHORT, + SUPPORTING_LANGUAGES, +} from '@/constants/common.i18n' +import { useUserContext } from '@/contexts/UserContext' + +import { useEntryContext } from '@/contexts/EntryContext' +import useGoTo from '@/hooks/useGoTo' +import useI18n from '@/hooks/useI18n' +import useSignOut from '@/hooks/useSignOut/useSignOut' +import { default as AUTH_TRANSLATIONS } from '@/pages/auth/auth.i18n' +import { Bars4Icon } from '@heroicons/react/24/outline' +import Link from 'next/link' +import { useRouter } from 'next/router' +import LifeLeaderIcon from 'public/logo/image-only.png' +import { useState } from 'react' +import Sidebar from './Sidebar' + +const Header = () => { + const { locale, locales } = useRouter() + const { isMobile } = useEntryContext() + const { changeLanguage, getTranslation } = useI18n() + const signOut = useSignOut() + const commonTranslation = getTranslation(COMMON_TRANSLATIONS) + const authTranslation = getTranslation(AUTH_TRANSLATIONS) + const { + user: { nickname }, + isSignedIn, + } = useUserContext() + const handleSignOut = async () => { + await signOut() + } + const { goTo } = useGoTo() + + const [isSidebarOpen, setSidebarOpen] = useState(false) + const toggleSidebar = () => setSidebarOpen(!isSidebarOpen) + + return ( + <> +
+
goTo('/')} + style={{ cursor: 'pointer' }} + > + Logo + + Life Leader + +
+
+ {!isMobile && ( + <> + {/* Page Navigation */} +
+ {isSignedIn && ( + + {commonTranslation('YourCharts')} + + )} + + {commonTranslation('CreateChart')} + +
+ {/* Authentication Section */} + {isSignedIn ? ( +
+ + {nickname} + + +
+ ) : ( + + {authTranslation('signIn')} + + )} + {/* Language Selector */} + + + )} + +
+
+ setSidebarOpen(false)} + /> + + ) +} + +export default Header diff --git a/packages/web/src/components/Layout/Sidebar.tsx b/packages/web/src/components/Layout/Sidebar.tsx new file mode 100644 index 0000000..c8bda9f --- /dev/null +++ b/packages/web/src/components/Layout/Sidebar.tsx @@ -0,0 +1,105 @@ +import { + COMMON_TRANSLATIONS, + LANGUAGE_NAMES_SHORT, + SUPPORTING_LANGUAGES, +} from '@/constants/common.i18n' +import { useUserContext } from '@/contexts/UserContext' +import useI18n from '@/hooks/useI18n' +import useSignOut from '@/hooks/useSignOut/useSignOut' +import { default as AUTH_TRANSLATIONS } from '@/pages/auth/auth.i18n' +import { XMarkIcon } from '@heroicons/react/24/outline' +import Link from 'next/link' + +interface SidebarProps { + isSidebarOpen: boolean + closeSidebar: () => void +} + +const Sidebar = ({ isSidebarOpen, closeSidebar }: SidebarProps) => { + const { currentLanguage, changeLanguage } = useI18n() + const { getTranslation } = useI18n() + const commonTranslation = getTranslation(COMMON_TRANSLATIONS) + const authTranslation = getTranslation(AUTH_TRANSLATIONS) + + const { isSignedIn } = useUserContext() + const signOut = useSignOut() + const handleSignOut = async () => { + await signOut() + closeSidebar() + } + + return ( +
+
+
+ Navigation + +
+ + {/* Navigation Links */} + + + {/* Language Selector */} +
+ Choose Language + +
+
+
+ ) +} + +export default Sidebar diff --git a/packages/web/src/components/MandalaChart/DisplayingFullViewMandalaChart.tsx b/packages/web/src/components/MandalaChart/DisplayingFullViewMandalaChart.tsx index efd9807..248f2a4 100644 --- a/packages/web/src/components/MandalaChart/DisplayingFullViewMandalaChart.tsx +++ b/packages/web/src/components/MandalaChart/DisplayingFullViewMandalaChart.tsx @@ -1,16 +1,38 @@ +import { useTheme } from '@/contexts/ThemeContext' +import { + getGridByIndexFromCreateMandalaChartInput, + getSquareByIndexFromMandalaCellInput, +} from '@/hooks/useMandalaChart.tsx/useMandalaChart.helper' +import { CreateMandalaChartInput } from '../../../gql/graphql' import Square, { SquareType } from './Square' interface Props { - wholeGridValues: string[][] + wholeGridValues: CreateMandalaChartInput } const DisplayingFullViewMandalaChart = ({ wholeGridValues }: Props) => { + const { themeStyle } = useTheme() + return ( -
- {wholeGridValues.map((values, gridIndex) => { +
+ {new Array(9).fill('').map((values, gridIndex) => { + const grid = getGridByIndexFromCreateMandalaChartInput( + wholeGridValues, + gridIndex + ) return ( -
- {values.map((value, suqareIndex) => { +
+ {new Array(9).fill('').map((_, suqareIndex) => { + const value = getSquareByIndexFromMandalaCellInput( + grid, + suqareIndex + ) + return ( void - handleGridValueOnAIMode: (gridIndex: number, squareIndex: number) => void - gridIndex: number - isAIModeOn: boolean -} - -const Grid = ({ - wholeGridValues, - handleGridValue, - gridIndex, - handleGridValueOnAIMode, - isAIModeOn, -}: GridProps) => { - const isGridValid = - gridIndex === 4 ? true : wholeGridValues[4][gridIndex] !== '' - const values = wholeGridValues[gridIndex] - const { getTranslation } = useI18n() - const translation = getTranslation(TRANSLATIONS) - - const getSquarePlaceHolder = ( - isCenterGrid: boolean, - isCenterSquare: boolean, - gridIndex: number, - squareIndex: number - ) => { - if (isCenterGrid && isCenterSquare) return translation('mainGoal') - if (isCenterGrid && !isCenterSquare) - return `${translation('subGoal')} ${ - squareIndex < 4 ? squareIndex + 1 : squareIndex - }` - if (!isCenterGrid && isCenterSquare) - return `${translation('subGoal')} ${ - gridIndex < 4 ? gridIndex + 1 : gridIndex - }` - return '' - } - - const isCenterGrid = gridIndex === 4 - return ( -
- {values.map((value, squareIndex) => { - const isCenterSquare = squareIndex === 4 - - const placeHolder = getSquarePlaceHolder( - isCenterGrid, - isCenterSquare, - gridIndex, - squareIndex - ) - - return ( - - ) - })} -
- ) -} - -export default Grid diff --git a/packages/web/src/components/MandalaChart/MandalaChart.tsx b/packages/web/src/components/MandalaChart/MandalaChart.tsx index 1db045f..3c13df7 100644 --- a/packages/web/src/components/MandalaChart/MandalaChart.tsx +++ b/packages/web/src/components/MandalaChart/MandalaChart.tsx @@ -1,15 +1,22 @@ -import { useAlert } from '@/contexts/AlertContext' import { useEntryContext } from '@/contexts/EntryContext' -import useI18n from '@/hooks/useI18n' +import { useTheme } from '@/contexts/ThemeContext' +import { + getGridByIndexFromMandalaChartInput, + getValueByGridIndexAndSquareIndexFromMandalaChartInput, +} from '@/hooks/useMandalaChart.tsx/useMandalaChart.helper' import { RecommendationCard } from '@/hooks/useRecommendationCard' -import { Dispatch, useEffect, useRef } from 'react' -import { deepCopy } from '../../../utils/common' -import Grid from './Grid' -import TRANSLATIONS from './MandalaChart.i18n' +import { useEffect, useRef } from 'react' +import { CreateMandalaChartInput } from '../../../gql/graphql' +import Square, { SquareType } from './Square' interface Props { - wholeGridValues: string[][] - setWholeGridValues: Dispatch> + wholeGridValues: CreateMandalaChartInput + handleSquareValueManually: ( + gridIndex: number, + squareIndex: number, + newValue: string + ) => void + handleSquareValueOnAIMode: (gridIndex: number, squareIndex: number) => void isAIModeOn: boolean recommendationItems: RecommendationCard[] onRecommendItemAccepted: () => void @@ -17,53 +24,13 @@ interface Props { const MandalaChart = ({ wholeGridValues, - setWholeGridValues, + handleSquareValueManually, + handleSquareValueOnAIMode, isAIModeOn, - recommendationItems, - onRecommendItemAccepted, }: Props) => { const { isMobile } = useEntryContext() const focusRef = useRef(null) - const { openAlert } = useAlert() - const { getTranslation } = useI18n() - const translation = getTranslation(TRANSLATIONS) - - const handleGridValue = ( - gridIndex: number, - squareIndex: number, - newValue: string - ) => { - setWholeGridValues(prevGridValue => { - const newGridValues = deepCopy(prevGridValue) - newGridValues[gridIndex][squareIndex] = newValue - if (gridIndex === 4 && squareIndex !== 4) { - newGridValues[squareIndex][gridIndex] = newValue - } - return newGridValues - }) - } - - const handleGridValueOnAIMode = (gridIndex: number, squareIndex: number) => { - const selectedAIRecommendationItem = recommendationItems.find( - item => item.isClicked - ) - - setWholeGridValues(prevGridValue => { - const newGridValues = deepCopy(prevGridValue) - if (gridIndex === 4 && squareIndex === 4) { - openAlert({ text: translation('cannotRecommendMainGoal') }) - } else { - newGridValues[gridIndex][squareIndex] = - selectedAIRecommendationItem?.text - if (gridIndex === 4 && squareIndex !== 4) { - newGridValues[squareIndex][gridIndex] = - selectedAIRecommendationItem?.text - } - onRecommendItemAccepted() - } - return newGridValues - }) - } + const { themeStyle } = useTheme() const scrollMainGoalSquareToCenter = () => { const container = focusRef.current @@ -72,7 +39,6 @@ const MandalaChart = ({ const containerRect = container.getBoundingClientRect() const elementRect = mainGoalElement.getBoundingClientRect() - // Calculating the position to scroll to const scrollLeft = elementRect.left + window.scrollX - @@ -84,7 +50,6 @@ const MandalaChart = ({ containerRect.top - (containerRect.height / 2 - elementRect.height / 2) - // Scrolling the container to center the element container.scrollLeft = scrollLeft container.scrollTop = scrollTop } @@ -101,17 +66,45 @@ const MandalaChart = ({ } overflow-auto`} ref={focusRef} > -
- {wholeGridValues.map((_, index) => ( - - ))} +
+ {new Array(9).fill('').map((values, gridIndex) => { + const grid = getGridByIndexFromMandalaChartInput( + wholeGridValues, + gridIndex + ) + const isGridValid = gridIndex === 4 ? true : grid.goal !== '' + + return ( +
+ {new Array(9).fill('').map((_, suqareIndex) => { + const value = + getValueByGridIndexAndSquareIndexFromMandalaChartInput( + wholeGridValues, + gridIndex, + suqareIndex + ) + + return ( + + ) + })} +
+ ) + })}
) diff --git a/packages/web/src/components/MandalaChart/Square.tsx b/packages/web/src/components/MandalaChart/Square.tsx index 2e2e1de..c9da187 100644 --- a/packages/web/src/components/MandalaChart/Square.tsx +++ b/packages/web/src/components/MandalaChart/Square.tsx @@ -1,7 +1,10 @@ import { useEntryContext } from '@/contexts/EntryContext' import { useTheme } from '@/contexts/ThemeContext' +import useI18n from '@/hooks/useI18n' import useModal from '@/hooks/useModal' +import { useRef } from 'react' import TextInputModal from '../Modal/TextInput' +import TRANSLATIONS from './MandalaChart.i18n' export enum SquareType { DISPLAY = 'DISPLAY', @@ -24,7 +27,7 @@ export interface ManualSquareProps { squareIndex: number isGridValid: boolean placeHolder?: string - handleGridValue: ( + handleSquareValueManually: ( gridIndex: number, squareIndex: number, newValue: string @@ -38,18 +41,20 @@ export interface AISquareProps { squareIndex: number isGridValid: boolean placeHolder?: string - handleGridValueOnAIMode: (gridIndex: number, squareIndex: number) => void + handleSquareValueOnAIMode: (gridIndex: number, squareIndex: number) => void } export type SquareProps = DisplaySquareProps | ManualSquareProps | AISquareProps const Square = (props: SquareProps) => { const { type, value, gridIndex, squareIndex, isGridValid } = props + const { getTranslation } = useI18n() + const translation = getTranslation(TRANSLATIONS) + const outlineRef = useRef(null) - // ManualSquareProps const setSquareValue = (newValue: string) => { if (type === SquareType.MANUAL) - props.handleGridValue(gridIndex, squareIndex, newValue) + props.handleSquareValueManually(gridIndex, squareIndex, newValue) } const { openModal: openTextInputModal, @@ -60,12 +65,19 @@ const Square = (props: SquareProps) => { state: value, setState: setSquareValue, }, + onModalClose: () => { + outlineRef.current?.classList.remove('border-4') + outlineRef.current?.classList.remove(themeStyle.highlightBorder) + }, }) const onClickSquare = () => { + if (!isGridValid) return if (type === SquareType.AI) { - props.handleGridValueOnAIMode(gridIndex, squareIndex) + props.handleSquareValueOnAIMode(gridIndex, squareIndex) } else { + outlineRef.current?.classList.add('border-4') + outlineRef.current?.classList.add(themeStyle.highlightBorder) // TODO: useModal에 들어가는 modalProps가 변경이 되지 않아 open시에 넣어주는 방식으로 우선 적용 openTextInputModal({ state: value }) } @@ -76,7 +88,7 @@ const Square = (props: SquareProps) => { const { themeStyle } = useTheme() const { isMobile } = useEntryContext() - const textBold = isCenterSquare ? 'font-bold' : '' + const textStyle = isCenterSquare ? 'font-bold' : '' const textColor = !isCenterSquare ? themeStyle.defualtTextColor @@ -84,10 +96,29 @@ const Square = (props: SquareProps) => { ? themeStyle.centerGridCenterSquareTextColor : themeStyle.edgeGridCenterSquareTextColor - const placeHolder = - type === SquareType.MANUAL || type === SquareType.AI - ? props.placeHolder - : '' + const getSquarePlaceHolder = () => { + if (isCenterGrid && isCenterSquare) return translation('mainGoal') + if (isCenterGrid && !isCenterSquare) + return `${translation('subGoal')} ${ + squareIndex < 4 ? squareIndex + 1 : squareIndex + }` + if (!isCenterGrid && isCenterSquare) + return `${translation('subGoal')} ${ + gridIndex < 4 ? gridIndex + 1 : gridIndex + }` + return '' + } + + const getTextOpacity = () => { + if (!value) { + if (isCenterSquare) return 'text-opacity-50' + if (isCenterGrid) return 'text-opacity-25' + } + return '' + } + + const placeHolder = getSquarePlaceHolder() + const opacity = getTextOpacity() return (
{ themeStyle.backgroundColor } ${isGridValid ? 'cursor-text' : 'bg-opacity-25'}`} onClick={() => onClickSquare()} + ref={outlineRef} > { const { theme, setTheme } = useTheme() const { getTranslation } = useI18n() - const trasnlate = getTranslation(TRANSLATIONS) + const translate = getTranslation(TRANSLATIONS) const mandalaThemes = Object.values(MandalaTheme) const options = mandalaThemes.map(mandalaTheme => ({ value: mandalaTheme, @@ -28,8 +28,16 @@ const MandalaThemeSelector = () => { return (
-