From 310a89fd70014641f3d38897365c5e7e0f847cbb Mon Sep 17 00:00:00 2001 From: cptrodgers Date: Sun, 12 May 2024 20:05:34 +0700 Subject: [PATCH] refactor: Change join space by invite url flow --- apps/ikigai/components/PreJoinSpace.tsx | 109 +++--- apps/ikigai/graphql/graphql-schema.json | 47 +-- apps/ikigai/graphql/mutation/SpaceMutation.ts | 21 +- apps/ikigai/graphql/types.ts | 49 +-- apps/ikigai/hook/UseUpdateDocument.tsx | 6 +- apps/ikigai/locales/en/messages.js | 2 +- apps/ikigai/locales/en/messages.po | 313 ++++++++++-------- apps/ikigai/storage/TokenStorage.ts | 2 +- graphql_server/src/db/notification.rs | 20 +- .../graphql/space_action/space_mutation.rs | 60 ++-- .../src/graphql/user_action/user_mutation.rs | 15 +- graphql_server/src/helper/authorize_helper.rs | 3 +- graphql_server/src/helper/mod.rs | 22 +- graphql_server/src/util/url_util.rs | 6 +- 14 files changed, 324 insertions(+), 351 deletions(-) diff --git a/apps/ikigai/components/PreJoinSpace.tsx b/apps/ikigai/components/PreJoinSpace.tsx index e91ad8b8..5df75cec 100644 --- a/apps/ikigai/components/PreJoinSpace.tsx +++ b/apps/ikigai/components/PreJoinSpace.tsx @@ -1,31 +1,35 @@ -import {useRouter} from "next/router"; -import {useState} from "react"; -import {Button, Divider, Input, Typography} from "antd"; -import {t, Trans} from "@lingui/macro"; +import { useRouter } from "next/router"; +import { useState } from "react"; +import { Button, Divider, Input, Typography } from "antd"; +import { t, Trans } from "@lingui/macro"; import useAuthUserStore from "../context/ZustandAuthStore"; -import {useMutation} from "@apollo/client"; -import {JOIN_SPACE_BY_INVITE_TOKEN} from "../graphql/mutation/SpaceMutation"; -import {handleError} from "../graphql/ApolloClient"; +import { useMutation } from "@apollo/client"; +import { JOIN_SPACE_BY_INVITE_TOKEN } from "../graphql/mutation/SpaceMutation"; +import { handleError } from "../graphql/ApolloClient"; import validator from "validator"; import toast from "react-hot-toast"; -import {JoinSpaceByInviteToken} from "../graphql/types"; -import TokenStorage from "../storage/TokenStorage"; -import {formatDocumentRoute} from "../config/Routes"; +import { JoinSpaceByInviteToken } from "../graphql/types"; const PreJoinSpace = () => { const router = useRouter(); - const currentEmail = useAuthUserStore(state => state.currentUser?.userMe?.email); + const currentEmail = useAuthUserStore( + (state) => state.currentUser?.userMe?.email, + ); const [email, setEmail] = useState(currentEmail || ""); - const [joinSpace, { loading }] = useMutation(JOIN_SPACE_BY_INVITE_TOKEN, { - onError: handleError, - }); - + const [joinSpace, { loading }] = useMutation( + JOIN_SPACE_BY_INVITE_TOKEN, + { + onError: handleError, + }, + ); + const [status, setStatus] = useState(); + const join = async () => { if (!validator.isEmail(email)) { toast.error(t`Wrong email format!`); return; } - + const { data } = await joinSpace({ variables: { email, @@ -33,38 +37,57 @@ const PreJoinSpace = () => { token: router.query.token, }, }); - + if (data) { - TokenStorage.set(data.spaceJoinByInviteToken.accessToken); - window.location.replace(formatDocumentRoute(data.spaceJoinByInviteToken.starterDocument.id)); - toast.success(t`Joined! We're moving you to your space...`); + setStatus(true); + toast.success(t`Joined! Please check your mail inbox`); + } else { + setStatus(false); } }; - + return (
- - You've been invited to join ikigai - - - Email - - setEmail(e.currentTarget.value)} - /> - - + {status === undefined && ( +
+ + You've been invited to join ikigai + + + Email + + setEmail(e.currentTarget.value)} + /> + + +
+ )} + {status !== undefined && ( +
+ + Joined! + + + + You've joined. We've sent a magic email to{" "} + {email}. Open the link in email + to access space. + + +
+ )}
); }; diff --git a/apps/ikigai/graphql/graphql-schema.json b/apps/ikigai/graphql/graphql-schema.json index ac34fc50..521c03d4 100644 --- a/apps/ikigai/graphql/graphql-schema.json +++ b/apps/ikigai/graphql/graphql-schema.json @@ -6777,8 +6777,8 @@ "kind": "NON_NULL", "name": null, "ofType": { - "kind": "OBJECT", - "name": "SpaceWithAccessToken", + "kind": "SCALAR", + "name": "Boolean", "ofType": null } }, @@ -8685,49 +8685,6 @@ "enumValues": null, "possibleTypes": null }, - { - "kind": "OBJECT", - "name": "SpaceWithAccessToken", - "description": null, - "fields": [ - { - "name": "starterDocument", - "description": null, - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "OBJECT", - "name": "Document", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "accessToken", - "description": null, - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "String", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, { "kind": "INPUT_OBJECT", "name": "NewComment", diff --git a/apps/ikigai/graphql/mutation/SpaceMutation.ts b/apps/ikigai/graphql/mutation/SpaceMutation.ts index 84a7aaad..d550e454 100644 --- a/apps/ikigai/graphql/mutation/SpaceMutation.ts +++ b/apps/ikigai/graphql/mutation/SpaceMutation.ts @@ -27,20 +27,6 @@ export const UPDATE_SPACE = gql` } `; -export const RESTORE_SPACE = gql` - mutation RestoreSpace($spaceId: Int!) { - spaceRestore(spaceId: $spaceId) { - id - } - } -`; - -export const DELETE_SPACE = gql` - mutation DeleteSpace($spaceId: Int!) { - spaceDelete(spaceId: $spaceId) - } -`; - export const GENERATE_SPACE_INVITE_TOKEN = gql` mutation GenerateSpaceInviteToken($data: SpaceInviteTokenInput!) { spaceGenerateInviteToken(data: $data) { @@ -62,11 +48,6 @@ export const JOIN_SPACE_BY_INVITE_TOKEN = gql` $spaceId: Int! $token: String! ) { - spaceJoinByInviteToken(email: $email, spaceId: $spaceId, token: $token) { - starterDocument { - id - } - accessToken - } + spaceJoinByInviteToken(email: $email, spaceId: $spaceId, token: $token) } `; diff --git a/apps/ikigai/graphql/types.ts b/apps/ikigai/graphql/types.ts index f340fb4a..2f169675 100644 --- a/apps/ikigai/graphql/types.ts +++ b/apps/ikigai/graphql/types.ts @@ -907,44 +907,6 @@ export interface UpdateSpaceVariables { // @generated // This file was automatically generated and should not be edited. -// ==================================================== -// GraphQL mutation operation: RestoreSpace -// ==================================================== - -export interface RestoreSpace_spaceRestore { - id: number; -} - -export interface RestoreSpace { - spaceRestore: RestoreSpace_spaceRestore; -} - -export interface RestoreSpaceVariables { - spaceId: number; -} - -/* tslint:disable */ -/* eslint-disable */ -// @generated -// This file was automatically generated and should not be edited. - -// ==================================================== -// GraphQL mutation operation: DeleteSpace -// ==================================================== - -export interface DeleteSpace { - spaceDelete: boolean; -} - -export interface DeleteSpaceVariables { - spaceId: number; -} - -/* tslint:disable */ -/* eslint-disable */ -// @generated -// This file was automatically generated and should not be edited. - // ==================================================== // GraphQL mutation operation: GenerateSpaceInviteToken // ==================================================== @@ -989,17 +951,8 @@ export interface DeleteSpaceInviteTokenVariables { // GraphQL mutation operation: JoinSpaceByInviteToken // ==================================================== -export interface JoinSpaceByInviteToken_spaceJoinByInviteToken_starterDocument { - id: any; -} - -export interface JoinSpaceByInviteToken_spaceJoinByInviteToken { - starterDocument: JoinSpaceByInviteToken_spaceJoinByInviteToken_starterDocument; - accessToken: string; -} - export interface JoinSpaceByInviteToken { - spaceJoinByInviteToken: JoinSpaceByInviteToken_spaceJoinByInviteToken; + spaceJoinByInviteToken: boolean; } export interface JoinSpaceByInviteTokenVariables { diff --git a/apps/ikigai/hook/UseUpdateDocument.tsx b/apps/ikigai/hook/UseUpdateDocument.tsx index 7dd7675b..713167b5 100644 --- a/apps/ikigai/hook/UseUpdateDocument.tsx +++ b/apps/ikigai/hook/UseUpdateDocument.tsx @@ -1,17 +1,21 @@ import { useMutation } from "@apollo/client"; -import { UpdateDocumentData } from "graphql/types"; +import { DocumentActionPermission, UpdateDocumentData } from "graphql/types"; import { UPDATE_DOCUMENT } from "graphql/mutation/SpaceMutation"; import { handleError } from "graphql/ApolloClient"; import useDocumentStore from "context/DocumentV2Store"; +import usePermission from "./UsePermission"; const UseUpdateDocument = () => { + const allow = usePermission(); const [updateDocumentServer] = useMutation(UPDATE_DOCUMENT, { onError: handleError, }); const activeDocument = useDocumentStore((state) => state.activeDocument); return (data: Partial) => { + if (!allow(DocumentActionPermission.EDIT_DOCUMENT)) return; + const updateDocumentData: UpdateDocumentData = { title: activeDocument.title, coverPhotoId: activeDocument.coverPhotoId, diff --git a/apps/ikigai/locales/en/messages.js b/apps/ikigai/locales/en/messages.js index fe03202a..7675161d 100644 --- a/apps/ikigai/locales/en/messages.js +++ b/apps/ikigai/locales/en/messages.js @@ -1 +1 @@ -/*eslint-disable*/module.exports={messages:JSON.parse("{\"wZPUJx\":[\"\\\"\",[\"0\"],\"\\\"\"],\"J/hVSQ\":[[\"0\"]],\"E8i2Y4\":[[\"devices\"],\" needs to be enabled\"],\"/41OE/\":[[\"groupName\"]],\"1daBru\":[[\"m\"]],\"6j5rE1\":[[\"name\"]],\"tEJjGm\":\"% of final grade\",\"OdTv2B\":\"% of total grade\",\"z6rdQi\":[\"<0>\",[\"0\"],\"% of grade\"],\"WC99K4\":\"Activation\",\"m16xKo\":\"Add\",\"uomLas\":\"Add category\",\"jEMFMd\":\"Add content\",\"DbGiEs\":\"Add Cover\",\"mqWXXZ\":\"Add criteria\",\"Q6QJvF\":\"Add level\",\"PCIlOO\":\"Add member\",\"b3s0x4\":\"Add org member successfully!\",\"nNCJ8r\":\"Add Organization Member\",\"SLX7MI\":\"Add sub content\",\"ZizAcM\":\"Add tag for category\",\"ejRlRe\":\"Add tag for document template\",\"dnkEIR\":\"Added at\",\"+lfY8Z\":\"After providing possible answers, select the correct one.\",\"xTA0qd\":\"After providing possible answers, select the correct options.\",\"uDEYUV\":\"ALL QUESTIONS\",\"0YV+NL\":\"Allow download\",\"5ufEdk\":\"Allow replay in submission\",\"4xBHUg\":\"Answer\",\"Dba5OX\":\"Answer explanation\",\"WPM2RV\":\"Answer type\",\"+qygei\":\"Answers\",\"RnvbQq\":[\"Are you sure to delete \",[\"title\"],\" permanently?\"],\"jCzXsE\":\"Are you sure to delete this class?\",\"GDC2oN\":\"Are you sure to delete this course?\",\"82pcKD\":[\"Are you sure to restore \",[\"title\"],\"?\"],\"tbA1Sz\":\"Are you sure want to delete this document permanently?\",\"2/b6h/\":\"Are you sure you want to delete this ?\",\"0dtKl9\":\"Assignment\",\"x1kF5w\":\"Assignment Settings\",\"HuoDHh\":\"Assignment type\",\"1qrjwm\":\"Attempt rules\",\"WEJZVy\":\"Attempt times\",\"R9Khdg\":\"Auto\",\"iH8pgl\":\"Back\",\"x4RuFo\":\"Back to home\",\"UNaXdI\":\"Back to templates\",\"egk7Zd\":\"Band score\",\"DdtpJh\":\"Band Score\",\"0wJVK+\":\"Basic\",\"jIPNJG\":\"Basic Information\",\"7s3WlU\":\"Blocks\",\"A3MAIm\":\"Branding\",\"vWEnQW\":\"Can not view this document\",\"dEgA5A\":\"Cancel\",\"d8P7Te\":\"Cannot duplicate class\",\"auVJal\":\"Cannot duplicate course\",\"e35d07\":\"Cannot process file\",\"Uz/xvR\":\"Change Avatar\",\"VA4cYs\":\"Change Email\",\"+3iFuu\":\"Change or Edit\",\"1nfMxB\":\"Check Your Email\",\"iZN9Kz\":\"Check your spam filter, or\",\"IzD6wu\":\"Choose another student\",\"ft6ass\":\"Class\",\"2koZ+P\":\"Classes\",\"F21nWs\":\"Click or drag file to this area to upload\",\"molX7u\":\"Collapse/Expand Left Side\",\"4KE3kR\":\"Collapse/Expand Right Side\",\"jZlrte\":\"Color\",\"+z+VqA\":\"COMMENTS\",\"r2A+Mi\":\"Confirm Delete Class\",\"UscwZL\":\"Confirm Delete Course\",\"yjkELF\":\"Confirm New Password\",\"p2/GCq\":\"Confirm Password\",\"v5+s2j\":\"Confirm password should be same with new password\",\"jfC/xh\":\"Contact\",\"kJ4Ohd\":\"Contact Media\",\"xGVfLh\":\"Continue\",\"RbXqEd\":\"Continue on last submission\",\"PiH3UR\":\"Copied!\",\"he3ygx\":\"Copy\",\"x7uIlL\":\"Copy invite link\",\"0TYQVp\":\"Copy public url\",\"my69WE\":\"Copy to class\",\"t2rBZB\":\"Correct\",\"MLax5Z\":\"Correct answer is\",\"Vfb/4U\":\"Correct answers\",\"x0HPLB\":\"Correct.\",\"jkvdko\":\"Correct. Explain\",\"ZVZvaF\":\"Correct. Explain:\",\"LxWsAd\":\"Create Class\",\"jnZoKa\":\"Create New Class\",\"ibGkxM\":\"Create new rubric\",\"m3DfE9\":\"Created!\",\"HM56Bx\":\"Creating...\",\"zelwwH\":\"Criteria\",\"DCKkhU\":\"Current Password\",\"ovBPCi\":\"Default\",\"cnGeoo\":\"Delete\",\"zBel3J\":\"Delete Avatar\",\"VhDbAh\":\"Delete Comment\",\"amEodY\":\"Delete content successfully!\",\"R1hbRW\":\"Delete Highlight\",\"AHveU+\":\"Delete permanently\",\"uNTuV6\":\"Deleted successfully!\",\"iGgVlf\":\"Deleted!\",\"Nu4oKW\":\"Description\",\"nXPoKw\":\"Description will show before your students start the submission\",\"F9WSux\":\"Did not receive the email?\",\"5KREqp\":\"Do you want delete this page block?\",\"MvOu0U\":\"Do you want delete this quiz?\",\"zzBXBW\":\"Do you want to apply this template to current document?\",\"ano/6X\":\"Do you want to delete member?\",\"sLqDdx\":\"Do you want to delete this block?\",\"NI++nr\":\"Do you want to delete this category?\",\"WTBkRZ\":\"Do you want to delete this document template?\",\"IYJLPW\":\"Do you want to delete this invite link\",\"mzuj7X\":\"Do you want to duplicate this document?\",\"FP8j54\":[\"Do you want to remove \",[\"0\"],\"?\"],\"UZ+0at\":[\"Do you want to remove \",[\"level\"],\"?\"],\"Gttb4m\":\"Do you want to reset this file upload?\",\"lOKHYn\":\"Do you want to restore this version?\",\"eGkK0V\":\"Do you want to save this document as template?\",\"7Zdnlq\":\"Document\",\"E/muDO\":\"Documents\",\"A3B8rx\":\"Don't have an account\",\"mzI/c+\":\"Download\",\"kxHZ/0\":\"Download (.csv)\",\"/wxtjO\":\"Downloadable\",\"euc6Ns\":\"Duplicate\",\"Gwwlha\":\"Duplicate to class\",\"Mq3q+1\":\"Duplicated!\",\"/bMMuE\":\"Duplicating...\",\"j1Jl7s\":\"Edit category\",\"W7Bg3R\":\"Edit Cover\",\"FtxbzV\":\"Edit information\",\"pZIqot\":\"Edit name\",\"UcNvSh\":\"Edit score\",\"bHboUQ\":\"Edit template\",\"FkQvI0\":\"Edit Template\",\"O3oNi5\":\"Email\",\"JpRseG\":\"Email or Phone Number\",\"xUgUTh\":\"Enter first name\",\"9/1YKL\":\"Enter last name\",\"f7sXvi\":\"Enter password\",\"fR9laE\":\"Enter your current password\",\"C3nD/1\":\"Enter your email\",\"LN2Y8T\":\"Enter your email or phone number to reset password\",\"5LDrMI\":\"Enter your last name\",\"0StR7t\":\"Enter your new password\",\"NgaR6B\":\"Enter your password\",\"LFNXuj\":\"Exit Full Screen\",\"1/ofid\":\"Expire\",\"ZHZieK\":\"Expire after\",\"M1RnFv\":\"Expired\",\"vzH+7Z\":\"Explain\",\"UnFIhG\":\"Explain the answer\",\"z2T/rk\":\"Explain your answer\",\"9lEpf7\":\"Failed to restore\",\"tOPrcS\":\"Failed!\",\"jRSIus\":\"Failed! Please try again.\",\"hrMr5i\":\"feedback\",\"YirHq7\":\"Feedback\",\"RjpTjV\":\"Feedback at\",\"7J9U4V\":\"Filter by added at\",\"H+KvOd\":\"Filter by role\",\"V1EGGU\":\"First name\",\"kODvZJ\":\"First Name\",\"j3gZw4\":\"Focus Mode\",\"hEPLrs\":\"Forgot password\",\"33hpQq\":\"From point\",\"nUaBWp\":\"Full Screen\",\"Weq9zb\":\"General\",\"ziAjHi\":\"Generate\",\"PWC5j6\":\"Generate Invite Link\",\"ZtKYrn\":\"Grade\",\"3BvRwX\":\"Graded\",\"mbRhek\":\"Graded!\",\"PisULm\":\"Hour : Minute : Second\",\"VEuIGR\":\"How about class title?\",\"7DedPo\":\"In Doing\",\"Oy0fs2\":\"Insert Blocks\",\"B2Tpo0\":\"Invalid email\",\"Cm0Tl2\":\"Invite token\",\"cj+X1c\":\"Inviter\",\"BHsrDx\":\"Invites\",\"Ve5Mjf\":\"Inviting role\",\"62x/cI\":\"Item are available for 60 days. After that time, item will be permanently deleted.\",\"0wdd7X\":\"Join\",\"sC2C42\":\"Joined! We're moving you to your space...\",\"1ZaQUH\":\"Last name\",\"UXBCwc\":\"Last Name\",\"J5fNv/\":\"Last name must be not empty\",\"exYcTF\":\"Library\",\"yRkqG9\":\"Limit\",\"TZ0q34\":\"Limit the number of times to take the assignment\",\"z0t9bb\":\"Login\",\"cuvnMe\":\"Login successfully!\",\"T4zr9t\":\"Login to your account\",\"nOhz3x\":\"Logout\",\"BWTzAb\":\"Manual\",\"O5hcpv\":\"Member since\",\"wlQNTg\":\"Members\",\"fiPesM\":\"Method\",\"8yAZsH\":\"Minimum length of organization name is 3. Maximum length of organization name is 100.\",\"H9HlDe\":\"minutes\",\"BNropb\":\"Mono\",\"ObZmB+\":\"More colors\",\"Jvv6rS\":\"Multiple Choice\",\"qhwV2C\":\"My Account\",\"hKtWk2\":\"My Profile\",\"6YtxFj\":\"Name\",\"qqeAJM\":\"Never\",\"H8yYW7\":\"New attempt\",\"LrqY0G\":\"New Attempt will remove answers in previous submission\",\"YMYqk6\":\"New Option\",\"7vhWI8\":\"New Password\",\"J0tDgh\":\"New password:\",\"ULy8pD\":\"No document\",\"qtoOYG\":\"No limit\",\"KWVo4e\":\"No students can access.\",\"YAfZXo\":\"Not Submitted\",\"zga9sT\":\"OK\",\"GQi2Ri\":\"Oops! We found a error.\",\"Ws0W5R\":\"ikigai\",\"vnaXLJ\":\"ikigai will auto calculate score and release to student.\",\"anHLGy\":\"ikigai will try to pre-calculate score of submissions. But teacher need to review submission and feedback to student.\",\"Yp+Hi/\":\"Open settings\",\"0zpgxV\":\"Options\",\"ucgZ0o\":\"Organization\",\"o0a7NJ\":\"Organization General\",\"65ye+h\":\"Organization Profile\",\"/IX/7x\":\"Other\",\"x9mRbl\":\"OTP Code:\",\"V15bkS\":\"OWNER\",\"RB01cP\":\"Page History\",\"8ZsakT\":\"Password\",\"AvGnzL\":\"Password is optional. We will generate random password for user in case you don‘t provide this information\",\"d50m97\":\"Password length must be greater than 8\",\"OUTo2Y\":\"Password length must be greater than 8 chars.\",\"uLcLBy\":\"Password must be 8 chars or longer\",\"3ny4xL\":\"Pasted file processing\",\"LK5hVs\":\"People can access this page via the address below.\",\"x/Iu08\":\"Please choose the correct answer and save again!\",\"f0jNfD\":\"Please enter your email\",\"KExnON\":\"Please enter your password\",\"ONCN28\":\"point\",\"dq45iC\":\"Powered by ikigai\",\"t7GpxE\":\"Pre-Assignment Descriptions\",\"FlOGRV\":\"Privacy & Terms\",\"zwBp5t\":\"Private\",\"vERlcd\":\"Profile\",\"k1GRrI\":\"Profile Setting\",\"7d1a0d\":\"Public\",\"wRHoSP\":\"Publish document\",\"bfgr/e\":\"Question\",\"K6TkGP\":\"questions\",\"enzGAL\":\"Questions\",\"XNJsAQ\":\"Quick create\",\"Ms84s0\":\"Quiz Settings\",\"0TGy7N\":\"Recently Deleted\",\"nHfxK5\":\"Recording Failed\",\"HpK/8d\":\"Reload\",\"ZNo5k1\":\"Remaining\",\"3fNwc3\":\"Remove Cover\",\"6V6NEa\":\"Remove Member\",\"l3c2be\":\"Remove member successfully!\",\"bV/tgs\":\"Replace record\",\"d2Q9qy\":\"Request student redo successfully!\",\"EHnwLY\":\"Request to redo\",\"8dg+Yo\":\"Required field\",\"OfhWJH\":\"Reset\",\"KbS2K9\":\"Reset Password\",\"luKGJn\":\"Reset password successfully!\",\"slOprG\":\"Reset your password\",\"yKu/3Y\":\"Restore\",\"2jjCPO\":\"Restored!\",\"oajJTs\":\"Restoring\",\"8Nd9Od\":\"Review & Grade\",\"lg5Uvz\":\"Review last submission\",\"JFXSHG\":\"Review setting quizzes\",\"GDvlUT\":\"Role\",\"FdrIIm\":\"Rubric\",\"DDlGWm\":\"RUBRIC\",\"/M7fb5\":\"Rubric Config\",\"zrRo1k\":\"Rubric Management\",\"2/33CO\":\"Rubric Type\",\"a13fiD\":\"Rubrics consist of rows and columns. The rows correspond to the criteria. The columns correspond to the level of achievement that describes each criterion.\",\"tfDRzk\":\"Save\",\"IpTHT6\":\"Save as Template\",\"s+OIU3\":\"Save document version\",\"AKZN8U\":\"Saved!\",\"uF9ruK\":\"Saving\",\"A1taO8\":\"Search\",\"zro6wS\":\"Security Settings\",\"P8g1G1\":\"Select a bandscore\",\"Rm+SZe\":\"Select and copy\",\"NJxEKR\":\"Select and Copy\",\"rKR8da\":\"Select Role\",\"vtZ80z\":\"Send email failed! Please try again.\",\"WLlxwe\":\"Send me a magic link!\",\"lPbtlE\":\"Sent. Please check your email inbox!\",\"e5bnvv\":\"Serif\",\"WynrT1\":\"Setting quizzes review\",\"Tz0i8g\":\"Settings\",\"SgY75v\":\"Setup the rubric for this assignment. Teacher will grade submissions by following the rubric criteria.\",\"Z8lGw6\":\"Share\",\"6F0sYE\":\"Single Choice\",\"Cj2Gtd\":\"Size\",\"pOE8Vh\":\"Space\",\"Bi/itF\":\"Space invite link\",\"QIwcdN\":\"Space name\",\"xfK3fA\":\"Space settings\",\"8d4a3S\":\"Start at\",\"yGSuAH\":\"Start audio recording\",\"6uPaO7\":\"Start video recording\",\"uAQUqI\":\"Status\",\"bZiZZw\":\"Structured Assignment\",\"a0qEkS\":\"Student\",\"ixyX1z\":\"Student can do anything, included setup of teacher.\",\"BifjM5\":\"Student will follow setup of teacher.\",\"Nz/2kI\":\"Students can access.\",\"1CalO6\":\"Style\",\"CWLRQH\":\"Styles\",\"W9KDpF\":\"Submission List\",\"hQRttt\":\"Submit\",\"1cukfH\":\"Submit at\",\"ZU3JQG\":\"Submitted\",\"KOUGSQ\":\"Successfully!\",\"yakvsK\":\"SUMMARY\",\"UL8A9w\":\"Tag\",\"OYHzN1\":\"Tags\",\"lypRAW\":\"Teacher\",\"iTylMl\":\"Templates\",\"I9qJ/Z\":\"Temporary Grade\",\"W6PxEF\":\"Test Duration\",\"xeiujy\":\"Text\",\"KSMsCC\":\"Text Styles\",\"Mtpjws\":\"The option that accepts input with sensitive (uppercase and lowercase) characters and trims leading and trailing spaces.\",\"3YtNBJ\":[\"There are some problems with this document. \",[\"fetchError\"]],\"VTDECb\":\"This action cannot be undone. Once you delete this, \\nyou will lose all the content and settings associated with it.\",\"Ag1Uvj\":\"This assignment will be counted into final grade.\",\"F8s/ay\":\"This assignment will not be counted into final grade.\",\"ZUimpV\":\"This document is in the trash.\",\"k9Brq2\":\"Time complete\",\"rnDURm\":\"To point\",\"+ZZp1U\":[\"To start recording your \",[\"0\"],\", please allow us to access your \",[\"1\"],\" by clicking 'Allow Access' button.\\nThis will allow your voice to be captured and used as part of your education experience\"],\"V/VigZ\":\"True, False, Not Given\",\"7yQXR4\":\"Try again!\",\"/diU8s\":\"try another email again!\",\"4E0Ozb\":\"Try to reload!\",\"+zy2Nq\":\"Type\",\"eSFTcb\":\"Type space name...\",\"WZcheX\":\"Type the limitation number\",\"h081HY\":\"Type to search\",\"jK1rnx\":\"Type to search class\",\"RLTc9G\":\"Type version name..\",\"DcXL9w\":\"Type your answer option\",\"eKLJsh\":\"Type your assignment description\",\"WPEELz\":\"Type your class name...\",\"w2aEFO\":\"Type your email\",\"HJYLfo\":\"Type your email!\",\"uU/kVZ\":\"Type your feedback\",\"oyY5zv\":\"Typing category name\",\"6ATCv0\":\"Typing criteria\",\"LP5Xic\":\"Typing level\",\"j6oyw0\":\"Typing template name\",\"yMKn+m\":\"Typing your description\",\"8qn4mF\":\"Ungraded\",\"Ef7StM\":\"Unknown\",\"NIuIk1\":\"Unlimited\",\"Jl5eaN\":\"Unlimited time\",\"wja8aL\":\"Untitled\",\"EkH9pt\":\"Update\",\"Uh/Lxa\":\"Update assignment successfully\",\"OyLhXN\":\"Update password successfully\",\"I+PHIE\":\"Updated!\",\"ONWvwQ\":\"Upload\",\"BNBfrU\":\"Upload Cover\",\"CuBkhi\":\"upload failed.\",\"IQ3gAw\":\"Upload File\",\"xfvWkE\":\"Upload Media\",\"JPjYLp\":\"uploaded successfully.\",\"Kucqjx\":\"Uploaded!\",\"QNtP5M\":\"Use template\",\"545XWN\":\"Use this rubric\",\"ekfzWq\":\"User Settings\",\"cOLei5\":\"Uses\",\"jpctdh\":\"View\",\"MxxFoU\":\"View and Edit\",\"dHbCaf\":\"Visible Mode\",\"bOfRYk\":\"We do not yet support the preview of this file\",\"H8yIzI\":[\"We have emailed you OTP Code to <0>\",[\"identity\"],\" to reset password.\"],\"vCUz9K\":\"We will send organization and access (includes password) information via email.\",\"24Rz2q\":\"We're developing it!!\",\"Cp8fKT\":\"welcome\",\"aFWU1n\":\"Width\",\"sU4kGJ\":\"Wrong email format!\",\"QKsZGR\":\"Wrong! Correct answer is\",\"K6OUTK\":\"Yes, No, Not Given\",\"yx4Yd2\":\"You cannot recover data if you confirm to delete.\",\"uebFEd\":\"You have no class here\",\"6GsOE0\":\"You've been invited to join ikigai\",\"Ti6gPo\":\"Your submission is completed. We will move you to assignment page!\",\"52R6eg\":\"ZoomIn\",\"W8KI/j\":\"ZoomOut\"}")}; +/*eslint-disable*/module.exports={messages:JSON.parse("{\"wZPUJx\":[\"\\\"\",[\"0\"],\"\\\"\"],\"J/hVSQ\":[[\"0\"]],\"E8i2Y4\":[[\"devices\"],\" needs to be enabled\"],\"/41OE/\":[[\"groupName\"]],\"1daBru\":[[\"m\"]],\"6j5rE1\":[[\"name\"]],\"tEJjGm\":\"% of final grade\",\"OdTv2B\":\"% of total grade\",\"z6rdQi\":[\"<0>\",[\"0\"],\"% of grade\"],\"WC99K4\":\"Activation\",\"m16xKo\":\"Add\",\"uomLas\":\"Add category\",\"jEMFMd\":\"Add content\",\"DbGiEs\":\"Add Cover\",\"mqWXXZ\":\"Add criteria\",\"Q6QJvF\":\"Add level\",\"PCIlOO\":\"Add member\",\"b3s0x4\":\"Add org member successfully!\",\"nNCJ8r\":\"Add Organization Member\",\"SLX7MI\":\"Add sub content\",\"ZizAcM\":\"Add tag for category\",\"ejRlRe\":\"Add tag for document template\",\"dnkEIR\":\"Added at\",\"+lfY8Z\":\"After providing possible answers, select the correct one.\",\"xTA0qd\":\"After providing possible answers, select the correct options.\",\"uDEYUV\":\"ALL QUESTIONS\",\"0YV+NL\":\"Allow download\",\"5ufEdk\":\"Allow replay in submission\",\"4xBHUg\":\"Answer\",\"Dba5OX\":\"Answer explanation\",\"WPM2RV\":\"Answer type\",\"+qygei\":\"Answers\",\"RnvbQq\":[\"Are you sure to delete \",[\"title\"],\" permanently?\"],\"jCzXsE\":\"Are you sure to delete this class?\",\"GDC2oN\":\"Are you sure to delete this course?\",\"82pcKD\":[\"Are you sure to restore \",[\"title\"],\"?\"],\"tbA1Sz\":\"Are you sure want to delete this document permanently?\",\"2/b6h/\":\"Are you sure you want to delete this ?\",\"kaEPzR\":\"Assign\",\"blfK/V\":\"Assigned!\",\"0dtKl9\":\"Assignment\",\"x1kF5w\":\"Assignment Settings\",\"HuoDHh\":\"Assignment type\",\"1qrjwm\":\"Attempt rules\",\"WEJZVy\":\"Attempt times\",\"R9Khdg\":\"Auto\",\"iH8pgl\":\"Back\",\"x4RuFo\":\"Back to home\",\"UNaXdI\":\"Back to templates\",\"egk7Zd\":\"Band score\",\"DdtpJh\":\"Band Score\",\"0wJVK+\":\"Basic\",\"jIPNJG\":\"Basic Information\",\"7s3WlU\":\"Blocks\",\"A3MAIm\":\"Branding\",\"vWEnQW\":\"Can not view this document\",\"dEgA5A\":\"Cancel\",\"d8P7Te\":\"Cannot duplicate class\",\"auVJal\":\"Cannot duplicate course\",\"e35d07\":\"Cannot process file\",\"Uz/xvR\":\"Change Avatar\",\"VA4cYs\":\"Change Email\",\"+3iFuu\":\"Change or Edit\",\"1nfMxB\":\"Check Your Email\",\"iZN9Kz\":\"Check your spam filter, or\",\"IzD6wu\":\"Choose another student\",\"ft6ass\":\"Class\",\"2koZ+P\":\"Classes\",\"F21nWs\":\"Click or drag file to this area to upload\",\"molX7u\":\"Collapse/Expand Left Side\",\"4KE3kR\":\"Collapse/Expand Right Side\",\"jZlrte\":\"Color\",\"+z+VqA\":\"COMMENTS\",\"r2A+Mi\":\"Confirm Delete Class\",\"UscwZL\":\"Confirm Delete Course\",\"yjkELF\":\"Confirm New Password\",\"p2/GCq\":\"Confirm Password\",\"v5+s2j\":\"Confirm password should be same with new password\",\"jfC/xh\":\"Contact\",\"kJ4Ohd\":\"Contact Media\",\"4b3oEV\":\"Content\",\"xGVfLh\":\"Continue\",\"RbXqEd\":\"Continue on last submission\",\"PiH3UR\":\"Copied!\",\"he3ygx\":\"Copy\",\"x7uIlL\":\"Copy invite link\",\"0TYQVp\":\"Copy public url\",\"my69WE\":\"Copy to class\",\"t2rBZB\":\"Correct\",\"MLax5Z\":\"Correct answer is\",\"Vfb/4U\":\"Correct answers\",\"x0HPLB\":\"Correct.\",\"jkvdko\":\"Correct. Explain\",\"ZVZvaF\":\"Correct. Explain:\",\"LxWsAd\":\"Create Class\",\"jnZoKa\":\"Create New Class\",\"ibGkxM\":\"Create new rubric\",\"m3DfE9\":\"Created!\",\"HM56Bx\":\"Creating...\",\"zelwwH\":\"Criteria\",\"DCKkhU\":\"Current Password\",\"ovBPCi\":\"Default\",\"cnGeoo\":\"Delete\",\"zBel3J\":\"Delete Avatar\",\"VhDbAh\":\"Delete Comment\",\"amEodY\":\"Delete content successfully!\",\"R1hbRW\":\"Delete Highlight\",\"AHveU+\":\"Delete permanently\",\"uNTuV6\":\"Deleted successfully!\",\"iGgVlf\":\"Deleted!\",\"Nu4oKW\":\"Description\",\"nXPoKw\":\"Description will show before your students start the submission\",\"F9WSux\":\"Did not receive the email?\",\"5KREqp\":\"Do you want delete this page block?\",\"MvOu0U\":\"Do you want delete this quiz?\",\"zzBXBW\":\"Do you want to apply this template to current document?\",\"ano/6X\":\"Do you want to delete member?\",\"sLqDdx\":\"Do you want to delete this block?\",\"NI++nr\":\"Do you want to delete this category?\",\"WTBkRZ\":\"Do you want to delete this document template?\",\"IYJLPW\":\"Do you want to delete this invite link\",\"mzuj7X\":\"Do you want to duplicate this document?\",\"FP8j54\":[\"Do you want to remove \",[\"0\"],\"?\"],\"UZ+0at\":[\"Do you want to remove \",[\"level\"],\"?\"],\"Gttb4m\":\"Do you want to reset this file upload?\",\"lOKHYn\":\"Do you want to restore this version?\",\"eGkK0V\":\"Do you want to save this document as template?\",\"7Zdnlq\":\"Document\",\"E/muDO\":\"Documents\",\"A3B8rx\":\"Don't have an account\",\"mzI/c+\":\"Download\",\"kxHZ/0\":\"Download (.csv)\",\"/wxtjO\":\"Downloadable\",\"euc6Ns\":\"Duplicate\",\"Gwwlha\":\"Duplicate to class\",\"Mq3q+1\":\"Duplicated!\",\"/bMMuE\":\"Duplicating...\",\"j1Jl7s\":\"Edit category\",\"W7Bg3R\":\"Edit Cover\",\"FtxbzV\":\"Edit information\",\"pZIqot\":\"Edit name\",\"UcNvSh\":\"Edit score\",\"bHboUQ\":\"Edit template\",\"FkQvI0\":\"Edit Template\",\"O3oNi5\":\"Email\",\"JpRseG\":\"Email or Phone Number\",\"xUgUTh\":\"Enter first name\",\"9/1YKL\":\"Enter last name\",\"f7sXvi\":\"Enter password\",\"fR9laE\":\"Enter your current password\",\"C3nD/1\":\"Enter your email\",\"LN2Y8T\":\"Enter your email or phone number to reset password\",\"5LDrMI\":\"Enter your last name\",\"0StR7t\":\"Enter your new password\",\"NgaR6B\":\"Enter your password\",\"LFNXuj\":\"Exit Full Screen\",\"1/ofid\":\"Expire\",\"ZHZieK\":\"Expire after\",\"M1RnFv\":\"Expired\",\"vzH+7Z\":\"Explain\",\"UnFIhG\":\"Explain the answer\",\"z2T/rk\":\"Explain your answer\",\"9lEpf7\":\"Failed to restore\",\"tOPrcS\":\"Failed!\",\"jRSIus\":\"Failed! Please try again.\",\"hrMr5i\":\"feedback\",\"YirHq7\":\"Feedback\",\"RjpTjV\":\"Feedback at\",\"7J9U4V\":\"Filter by added at\",\"H+KvOd\":\"Filter by role\",\"V1EGGU\":\"First name\",\"kODvZJ\":\"First Name\",\"j3gZw4\":\"Focus Mode\",\"kNqMMd\":\"Folder\",\"hEPLrs\":\"Forgot password\",\"33hpQq\":\"From point\",\"nUaBWp\":\"Full Screen\",\"Weq9zb\":\"General\",\"ziAjHi\":\"Generate\",\"PWC5j6\":\"Generate Invite Link\",\"ZtKYrn\":\"Grade\",\"3BvRwX\":\"Graded\",\"mbRhek\":\"Graded!\",\"PisULm\":\"Hour : Minute : Second\",\"VEuIGR\":\"How about class title?\",\"v6J8p3\":\"ikigai\",\"8iEVQA\":\"ikigai will auto calculate score and release to student.\",\"RZ5dfl\":\"ikigai will try to pre-calculate score of submissions. But teacher need to review submission and feedback to student.\",\"7DedPo\":\"In Doing\",\"Oy0fs2\":\"Insert Blocks\",\"B2Tpo0\":\"Invalid email\",\"Cm0Tl2\":\"Invite token\",\"cj+X1c\":\"Inviter\",\"BHsrDx\":\"Invites\",\"Ve5Mjf\":\"Inviting role\",\"62x/cI\":\"Item are available for 60 days. After that time, item will be permanently deleted.\",\"0wdd7X\":\"Join\",\"JF1DRX\":\"Joined!\",\"i9C2AV\":\"Joined! Please check your mail inbox\",\"sC2C42\":\"Joined! We're moving you to your space...\",\"1ZaQUH\":\"Last name\",\"UXBCwc\":\"Last Name\",\"J5fNv/\":\"Last name must be not empty\",\"exYcTF\":\"Library\",\"yRkqG9\":\"Limit\",\"TZ0q34\":\"Limit the number of times to take the assignment\",\"z0t9bb\":\"Login\",\"cuvnMe\":\"Login successfully!\",\"T4zr9t\":\"Login to your account\",\"nOhz3x\":\"Logout\",\"BWTzAb\":\"Manual\",\"O5hcpv\":\"Member since\",\"wlQNTg\":\"Members\",\"fiPesM\":\"Method\",\"8yAZsH\":\"Minimum length of organization name is 3. Maximum length of organization name is 100.\",\"H9HlDe\":\"minutes\",\"BNropb\":\"Mono\",\"ObZmB+\":\"More colors\",\"Jvv6rS\":\"Multiple Choice\",\"qhwV2C\":\"My Account\",\"hKtWk2\":\"My Profile\",\"6YtxFj\":\"Name\",\"qqeAJM\":\"Never\",\"H8yYW7\":\"New attempt\",\"LrqY0G\":\"New Attempt will remove answers in previous submission\",\"YMYqk6\":\"New Option\",\"7vhWI8\":\"New Password\",\"J0tDgh\":\"New password:\",\"ULy8pD\":\"No document\",\"qtoOYG\":\"No limit\",\"KWVo4e\":\"No students can access.\",\"YAfZXo\":\"Not Submitted\",\"zga9sT\":\"OK\",\"GQi2Ri\":\"Oops! We found a error.\",\"Yp+Hi/\":\"Open settings\",\"0zpgxV\":\"Options\",\"ucgZ0o\":\"Organization\",\"o0a7NJ\":\"Organization General\",\"65ye+h\":\"Organization Profile\",\"/IX/7x\":\"Other\",\"x9mRbl\":\"OTP Code:\",\"V15bkS\":\"OWNER\",\"RB01cP\":\"Page History\",\"8ZsakT\":\"Password\",\"AvGnzL\":\"Password is optional. We will generate random password for user in case you don‘t provide this information\",\"d50m97\":\"Password length must be greater than 8\",\"OUTo2Y\":\"Password length must be greater than 8 chars.\",\"uLcLBy\":\"Password must be 8 chars or longer\",\"3ny4xL\":\"Pasted file processing\",\"LK5hVs\":\"People can access this page via the address below.\",\"x/Iu08\":\"Please choose the correct answer and save again!\",\"f0jNfD\":\"Please enter your email\",\"KExnON\":\"Please enter your password\",\"ONCN28\":\"point\",\"zO7DEY\":\"Powered by ikigai\",\"t7GpxE\":\"Pre-Assignment Descriptions\",\"FlOGRV\":\"Privacy & Terms\",\"zwBp5t\":\"Private\",\"vERlcd\":\"Profile\",\"k1GRrI\":\"Profile Setting\",\"7d1a0d\":\"Public\",\"wRHoSP\":\"Publish document\",\"bfgr/e\":\"Question\",\"K6TkGP\":\"questions\",\"enzGAL\":\"Questions\",\"XNJsAQ\":\"Quick create\",\"Ms84s0\":\"Quiz Settings\",\"0TGy7N\":\"Recently Deleted\",\"nHfxK5\":\"Recording Failed\",\"HpK/8d\":\"Reload\",\"ZNo5k1\":\"Remaining\",\"22wiXL\":\"Remove assigned user\",\"3fNwc3\":\"Remove Cover\",\"6V6NEa\":\"Remove Member\",\"l3c2be\":\"Remove member successfully!\",\"bV/tgs\":\"Replace record\",\"d2Q9qy\":\"Request student redo successfully!\",\"EHnwLY\":\"Request to redo\",\"8dg+Yo\":\"Required field\",\"OfhWJH\":\"Reset\",\"KbS2K9\":\"Reset Password\",\"luKGJn\":\"Reset password successfully!\",\"slOprG\":\"Reset your password\",\"yKu/3Y\":\"Restore\",\"2jjCPO\":\"Restored!\",\"oajJTs\":\"Restoring\",\"8Nd9Od\":\"Review & Grade\",\"lg5Uvz\":\"Review last submission\",\"JFXSHG\":\"Review setting quizzes\",\"GDvlUT\":\"Role\",\"FdrIIm\":\"Rubric\",\"DDlGWm\":\"RUBRIC\",\"/M7fb5\":\"Rubric Config\",\"zrRo1k\":\"Rubric Management\",\"2/33CO\":\"Rubric Type\",\"a13fiD\":\"Rubrics consist of rows and columns. The rows correspond to the criteria. The columns correspond to the level of achievement that describes each criterion.\",\"tfDRzk\":\"Save\",\"IpTHT6\":\"Save as Template\",\"s+OIU3\":\"Save document version\",\"AKZN8U\":\"Saved!\",\"uF9ruK\":\"Saving\",\"A1taO8\":\"Search\",\"zro6wS\":\"Security Settings\",\"P8g1G1\":\"Select a bandscore\",\"Rm+SZe\":\"Select and copy\",\"NJxEKR\":\"Select and Copy\",\"rKR8da\":\"Select Role\",\"vtZ80z\":\"Send email failed! Please try again.\",\"WLlxwe\":\"Send me a magic link!\",\"lPbtlE\":\"Sent. Please check your email inbox!\",\"oGeEi8\":\"Seperated by commas or space\",\"e5bnvv\":\"Serif\",\"WynrT1\":\"Setting quizzes review\",\"Tz0i8g\":\"Settings\",\"SgY75v\":\"Setup the rubric for this assignment. Teacher will grade submissions by following the rubric criteria.\",\"Z8lGw6\":\"Share\",\"6F0sYE\":\"Single Choice\",\"Cj2Gtd\":\"Size\",\"pOE8Vh\":\"Space\",\"Bi/itF\":\"Space invite link\",\"QIwcdN\":\"Space name\",\"xfK3fA\":\"Space settings\",\"8d4a3S\":\"Start at\",\"yGSuAH\":\"Start audio recording\",\"6uPaO7\":\"Start video recording\",\"uAQUqI\":\"Status\",\"bZiZZw\":\"Structured Assignment\",\"a0qEkS\":\"Student\",\"ixyX1z\":\"Student can do anything, included setup of teacher.\",\"BifjM5\":\"Student will follow setup of teacher.\",\"Nz/2kI\":\"Students can access.\",\"1CalO6\":\"Style\",\"CWLRQH\":\"Styles\",\"W9KDpF\":\"Submission List\",\"hQRttt\":\"Submit\",\"1cukfH\":\"Submit at\",\"ZU3JQG\":\"Submitted\",\"KOUGSQ\":\"Successfully!\",\"yakvsK\":\"SUMMARY\",\"UL8A9w\":\"Tag\",\"OYHzN1\":\"Tags\",\"lypRAW\":\"Teacher\",\"iTylMl\":\"Templates\",\"I9qJ/Z\":\"Temporary Grade\",\"W6PxEF\":\"Test Duration\",\"xeiujy\":\"Text\",\"KSMsCC\":\"Text Styles\",\"Mtpjws\":\"The option that accepts input with sensitive (uppercase and lowercase) characters and trims leading and trailing spaces.\",\"3YtNBJ\":[\"There are some problems with this document. \",[\"fetchError\"]],\"VTDECb\":\"This action cannot be undone. Once you delete this, \\nyou will lose all the content and settings associated with it.\",\"Ag1Uvj\":\"This assignment will be counted into final grade.\",\"F8s/ay\":\"This assignment will not be counted into final grade.\",\"ZUimpV\":\"This document is in the trash.\",\"k9Brq2\":\"Time complete\",\"rnDURm\":\"To point\",\"+ZZp1U\":[\"To start recording your \",[\"0\"],\", please allow us to access your \",[\"1\"],\" by clicking 'Allow Access' button.\\nThis will allow your voice to be captured and used as part of your education experience\"],\"V/VigZ\":\"True, False, Not Given\",\"7yQXR4\":\"Try again!\",\"/diU8s\":\"try another email again!\",\"4E0Ozb\":\"Try to reload!\",\"+zy2Nq\":\"Type\",\"eSFTcb\":\"Type space name...\",\"WZcheX\":\"Type the limitation number\",\"h081HY\":\"Type to search\",\"jK1rnx\":\"Type to search class\",\"RLTc9G\":\"Type version name..\",\"DcXL9w\":\"Type your answer option\",\"eKLJsh\":\"Type your assignment description\",\"WPEELz\":\"Type your class name...\",\"w2aEFO\":\"Type your email\",\"HJYLfo\":\"Type your email!\",\"uU/kVZ\":\"Type your feedback\",\"oyY5zv\":\"Typing category name\",\"6ATCv0\":\"Typing criteria\",\"LP5Xic\":\"Typing level\",\"j6oyw0\":\"Typing template name\",\"yMKn+m\":\"Typing your description\",\"8qn4mF\":\"Ungraded\",\"Ef7StM\":\"Unknown\",\"NIuIk1\":\"Unlimited\",\"Jl5eaN\":\"Unlimited time\",\"wja8aL\":\"Untitled\",\"EkH9pt\":\"Update\",\"Uh/Lxa\":\"Update assignment successfully\",\"OyLhXN\":\"Update password successfully\",\"I+PHIE\":\"Updated!\",\"ONWvwQ\":\"Upload\",\"BNBfrU\":\"Upload Cover\",\"CuBkhi\":\"upload failed.\",\"IQ3gAw\":\"Upload File\",\"xfvWkE\":\"Upload Media\",\"JPjYLp\":\"uploaded successfully.\",\"Kucqjx\":\"Uploaded!\",\"QNtP5M\":\"Use template\",\"545XWN\":\"Use this rubric\",\"ekfzWq\":\"User Settings\",\"cOLei5\":\"Uses\",\"jpctdh\":\"View\",\"MxxFoU\":\"View and Edit\",\"dHbCaf\":\"Visible Mode\",\"bOfRYk\":\"We do not yet support the preview of this file\",\"H8yIzI\":[\"We have emailed you OTP Code to <0>\",[\"identity\"],\" to reset password.\"],\"vCUz9K\":\"We will send organization and access (includes password) information via email.\",\"24Rz2q\":\"We're developing it!!\",\"Cp8fKT\":\"welcome\",\"Jgkpyy\":\"When a person is assigned to this document, they will receive receive an email granting access to the document and enrolling them as a space student.\",\"aFWU1n\":\"Width\",\"sU4kGJ\":\"Wrong email format!\",\"QKsZGR\":\"Wrong! Correct answer is\",\"K6OUTK\":\"Yes, No, Not Given\",\"yx4Yd2\":\"You cannot recover data if you confirm to delete.\",\"uebFEd\":\"You have no class here\",\"5/F5Qx\":\"You've been invited to join ikigai\",\"VytYHt\":[\"You've joined. We've sent a magic email to <0>\",[\"email\"],\". Open the link in email to access space.\"],\"Ti6gPo\":\"Your submission is completed. We will move you to assignment page!\",\"52R6eg\":\"ZoomIn\",\"W8KI/j\":\"ZoomOut\"}")}; \ No newline at end of file diff --git a/apps/ikigai/locales/en/messages.po b/apps/ikigai/locales/en/messages.po index 041261ee..47f2c14d 100644 --- a/apps/ikigai/locales/en/messages.po +++ b/apps/ikigai/locales/en/messages.po @@ -17,10 +17,10 @@ msgstr "" msgid "\"{0}\"" msgstr "\"{0}\"" -#: components/Document/DocumentMoreSetting.tsx:177 -#: components/Document/DocumentMoreSetting.tsx:187 -#: components/Document/DocumentMoreSetting.tsx:207 -#: components/Document/DocumentMoreSetting.tsx:220 +#: components/Document/DocumentMoreSetting.tsx:178 +#: components/Document/DocumentMoreSetting.tsx:188 +#: components/Document/DocumentMoreSetting.tsx:208 +#: components/Document/DocumentMoreSetting.tsx:221 #: components/Document/RightPanel/BlockMenu/defaultMenu.config.tsx:84 #: components/Document/RightPanel/BlockMenu/defaultMenu.config.tsx:97 #: components/Document/RightPanel/BlockMenu/defaultMenu.config.tsx:110 @@ -51,11 +51,11 @@ msgstr "{name}" msgid "% of final grade" msgstr "% of final grade" -#: components/Document/AssignmentSetting/AssignmentGradeSettingTab/Rubric.tsx:215 +#: components/Document/AssignmentSetting/AssignmentGradeSettingTab/Rubric.tsx:214 msgid "% of total grade" msgstr "% of total grade" -#: components/Document/AssignmentSetting/AssignmentGradeSettingTab/Rubric.tsx:455 +#: components/Document/AssignmentSetting/AssignmentGradeSettingTab/Rubric.tsx:454 msgid "<0>{0}% of grade" msgstr "<0>{0}% of grade" @@ -71,19 +71,22 @@ msgstr "Activation" #~ msgid "Add category" #~ msgstr "Add category" +#: components/common/LearningModuleDnd/CreateContentButton.tsx:130 #: components/Document/LeftPanel/CreateContentButton.tsx:108 +#: components/DocumentV2/LeftSide/index.tsx:75 msgid "Add content" msgstr "Add content" -#: components/Document/DocumentSetting.tsx:224 +#: components/Document/DocumentSetting.tsx:219 +#: components/DocumentV2/DocumentBody/CoverPage/CoverPhotoHeader.tsx:100 msgid "Add Cover" msgstr "Add Cover" -#: components/Document/AssignmentSetting/AssignmentGradeSettingTab/Rubric.tsx:474 +#: components/Document/AssignmentSetting/AssignmentGradeSettingTab/Rubric.tsx:473 msgid "Add criteria" msgstr "Add criteria" -#: components/Document/AssignmentSetting/AssignmentGradeSettingTab/Rubric.tsx:443 +#: components/Document/AssignmentSetting/AssignmentGradeSettingTab/Rubric.tsx:442 msgid "Add level" msgstr "Add level" @@ -99,6 +102,7 @@ msgstr "Add level" #~ msgid "Add Organization Member" #~ msgstr "Add Organization Member" +#: components/common/LearningModuleDnd/CreateContentButton.tsx:128 #: components/Document/LeftPanel/CreateContentButton.tsx:106 msgid "Add sub content" msgstr "Add sub content" @@ -154,8 +158,8 @@ msgid "Answers" msgstr "Answers" #: components/Trash/DeletedItem.tsx:54 -msgid "Are you sure to delete {title} permanently?" -msgstr "Are you sure to delete {title} permanently?" +#~ msgid "Are you sure to delete {title} permanently?" +#~ msgstr "Are you sure to delete {title} permanently?" #: components/ClassListManagement/MenuActions.tsx:66 #~ msgid "Are you sure to delete this class?" @@ -166,23 +170,34 @@ msgstr "Are you sure to delete {title} permanently?" #~ msgstr "Are you sure to delete this course?" #: components/Trash/DeletedItem.tsx:39 -msgid "Are you sure to restore {title}?" -msgstr "Are you sure to restore {title}?" +#~ msgid "Are you sure to restore {title}?" +#~ msgstr "Are you sure to restore {title}?" -#: components/Document/DocumentSetting.tsx:181 +#: components/Document/DocumentSetting.tsx:176 msgid "Are you sure want to delete this document permanently?" msgstr "Are you sure want to delete this document permanently?" -#: components/Document/DocumentMoreSetting.tsx:68 +#: components/Document/DocumentMoreSetting.tsx:69 #: components/Document/LeftPanel/LessonItem.tsx:124 msgid "Are you sure you want to delete this ?" msgstr "Are you sure you want to delete this ?" +#: components/Document/Assignment/AssignmentHeader.tsx:43 +#: components/Document/DocumentAssignedList.tsx:131 +#: components/Document/DocumentAssignedList.tsx:162 +msgid "Assign" +msgstr "Assign" + +#: components/Document/DocumentAssignedList.tsx:77 +msgid "Assigned!" +msgstr "Assigned!" + +#: components/common/LearningModuleDnd/CreateContentButton.tsx:99 #: components/Document/LeftPanel/CreateContentButton.tsx:77 msgid "Assignment" msgstr "Assignment" -#: components/Document/DocumentSetting.tsx:249 +#: components/Document/DocumentSetting.tsx:244 msgid "Assignment Settings" msgstr "Assignment Settings" @@ -208,7 +223,7 @@ msgstr "Auto" #: components/Document/ServerDocumentFetchError.tsx:30 #: components/ErrorBoundary.tsx:52 -#: components/Initializing.tsx:216 +#: components/Initializing.tsx:223 #: pages/404.tsx:23 msgid "Back to home" msgstr "Back to home" @@ -246,7 +261,7 @@ msgid "Can not view this document" msgstr "Can not view this document" #: components/common/AddResourceModal.tsx:121 -#: components/Document/DocumentMoreSetting.tsx:74 +#: components/Document/DocumentMoreSetting.tsx:75 #: components/Document/LeftPanel/LessonItem.tsx:130 msgid "Cancel" msgstr "Cancel" @@ -297,16 +312,16 @@ msgstr "Choose another student" #~ msgid "Classes" #~ msgstr "Classes" -#: components/common/FileUpload.tsx:181 +#: components/common/FileUpload.tsx:198 #: components/common/ManualFileUpload.tsx:50 msgid "Click or drag file to this area to upload" msgstr "Click or drag file to this area to upload" -#: components/Document/DocumentHeader.tsx:207 +#: components/Document/DocumentHeader.tsx:214 msgid "Collapse/Expand Left Side" msgstr "Collapse/Expand Left Side" -#: components/Document/DocumentHeader.tsx:221 +#: components/Document/DocumentHeader.tsx:233 msgid "Collapse/Expand Right Side" msgstr "Collapse/Expand Right Side" @@ -314,7 +329,7 @@ msgstr "Collapse/Expand Right Side" msgid "Color" msgstr "Color" -#: components/Document/RightPanel/FeedbackPanel.tsx:413 +#: components/Document/RightPanel/FeedbackPanel.tsx:410 msgid "COMMENTS" msgstr "COMMENTS" @@ -346,6 +361,10 @@ msgstr "COMMENTS" #~ msgid "Contact Media" #~ msgstr "Contact Media" +#: components/DocumentV2/LeftSide/index.tsx:71 +msgid "Content" +msgstr "Content" + #: components/ClassListManagement/CreateClassModal.tsx:99 #~ msgid "Continue" #~ msgstr "Continue" @@ -409,11 +428,11 @@ msgstr "Correct. Explain:" #~ msgid "Create New Class" #~ msgstr "Create New Class" -#: components/Document/AssignmentSetting/AssignmentGradeSettingTab/RubricManagement.tsx:123 +#: components/Document/AssignmentSetting/AssignmentGradeSettingTab/RubricManagement.tsx:120 msgid "Create new rubric" msgstr "Create new rubric" -#: components/Document/AssignmentSetting/AssignmentGradeSettingTab/RubricManagement.tsx:90 +#: components/Document/AssignmentSetting/AssignmentGradeSettingTab/RubricManagement.tsx:87 msgid "Created!" msgstr "Created!" @@ -421,7 +440,7 @@ msgstr "Created!" #~ msgid "Creating..." #~ msgstr "Creating..." -#: components/Document/AssignmentSetting/AssignmentGradeSettingTab/Rubric.tsx:204 +#: components/Document/AssignmentSetting/AssignmentGradeSettingTab/Rubric.tsx:203 msgid "Criteria" msgstr "Criteria" @@ -429,16 +448,16 @@ msgstr "Criteria" #~ msgid "Current Password" #~ msgstr "Current Password" -#: components/Document/DocumentMoreSetting.tsx:136 +#: components/Document/DocumentMoreSetting.tsx:137 msgid "Default" msgstr "Default" #: components/common/RichMarkdownEditor/BlockComponents/index.tsx:148 #: components/common/RichMarkdownEditor/extensions/CommonEmbedExtension/CommonEmbedDropdownMenu.tsx:20 #: components/common/RichMarkdownEditor/extensions/PageBlockExtension/PageBlock.tsx:292 -#: components/common/RichMarkdownEditor/extensions/QuizExtension/QuizSetting/QuizDropdownMenu.tsx:139 +#: components/common/RichMarkdownEditor/extensions/QuizExtension/QuizSetting/QuizDropdownMenu.tsx:138 #: components/common/RichMarkdownEditor/extensions/RecordExtension/RecordBlock.tsx:294 -#: components/Document/DocumentMoreSetting.tsx:242 +#: components/Document/DocumentMoreSetting.tsx:243 #: components/Document/LeftPanel/LessonItem.tsx:119 msgid "Delete" msgstr "Delete" @@ -447,11 +466,11 @@ msgstr "Delete" #~ msgid "Delete Avatar" #~ msgstr "Delete Avatar" -#: components/Document/RightPanel/FeedbackPanel.tsx:239 +#: components/Document/RightPanel/FeedbackPanel.tsx:238 msgid "Delete Comment" msgstr "Delete Comment" -#: components/Document/DocumentMoreSetting.tsx:78 +#: components/Document/DocumentMoreSetting.tsx:79 msgid "Delete content successfully!" msgstr "Delete content successfully!" @@ -459,8 +478,7 @@ msgstr "Delete content successfully!" msgid "Delete Highlight" msgstr "Delete Highlight" -#: components/Document/DocumentSetting.tsx:185 -#: components/Trash/DeletedItem.tsx:105 +#: components/Document/DocumentSetting.tsx:180 msgid "Delete permanently" msgstr "Delete permanently" @@ -468,11 +486,10 @@ msgstr "Delete permanently" #~ msgid "Deleted successfully!" #~ msgstr "Deleted successfully!" -#: components/Document/AssignmentSetting/AssignmentGradeSettingTab/RubricManagement.tsx:65 +#: components/Document/AssignmentSetting/AssignmentGradeSettingTab/RubricManagement.tsx:63 +#: components/Document/DocumentAssignedList.tsx:94 #: components/Document/LeftPanel/LessonItem.tsx:76 #: components/SpaceSetting/InviteSpaceSetting.tsx:48 -#: components/Trash/DeletedDocumentList.tsx:40 -#: components/Trash/DeletedSpaceList.tsx:40 msgid "Deleted!" msgstr "Deleted!" @@ -493,7 +510,7 @@ msgstr "Description will show before your students start the submission" msgid "Do you want delete this page block?" msgstr "Do you want delete this page block?" -#: components/common/RichMarkdownEditor/extensions/QuizExtension/QuizSetting/QuizDropdownMenu.tsx:108 +#: components/common/RichMarkdownEditor/extensions/QuizExtension/QuizSetting/QuizDropdownMenu.tsx:107 msgid "Do you want delete this quiz?" msgstr "Do you want delete this quiz?" @@ -521,16 +538,16 @@ msgstr "Do you want to delete this block?" msgid "Do you want to delete this invite link" msgstr "Do you want to delete this invite link" -#: components/Document/DocumentMoreSetting.tsx:55 +#: components/Document/DocumentMoreSetting.tsx:56 msgid "Do you want to duplicate this document?" msgstr "Do you want to duplicate this document?" -#: components/Document/AssignmentSetting/AssignmentGradeSettingTab/Rubric.tsx:354 -#: components/Document/AssignmentSetting/AssignmentGradeSettingTab/RubricManagement.tsx:178 +#: components/Document/AssignmentSetting/AssignmentGradeSettingTab/Rubric.tsx:353 +#: components/Document/AssignmentSetting/AssignmentGradeSettingTab/RubricManagement.tsx:175 msgid "Do you want to remove {0}?" msgstr "Do you want to remove {0}?" -#: components/Document/AssignmentSetting/AssignmentGradeSettingTab/Rubric.tsx:249 +#: components/Document/AssignmentSetting/AssignmentGradeSettingTab/Rubric.tsx:248 msgid "Do you want to remove {level}?" msgstr "Do you want to remove {level}?" @@ -547,11 +564,10 @@ msgstr "Do you want to reset this file upload?" #~ msgstr "Do you want to save this document as template?" #: components/Document/LeftPanel/CreateContentButton.tsx:72 -#: components/Trash/index.tsx:24 msgid "Document" msgstr "Document" -#: components/Document/LeftPanel/index.tsx:82 +#: components/Document/LeftPanel/index.tsx:83 msgid "Documents" msgstr "Documents" @@ -567,11 +583,11 @@ msgstr "Download" msgid "Download (.csv)" msgstr "Download (.csv)" -#: components/common/RichMarkdownEditor/extensions/FileExtension/FileBlock.tsx:295 +#: components/common/RichMarkdownEditor/extensions/FileExtension/FileBlock.tsx:293 msgid "Downloadable" msgstr "Downloadable" -#: components/Document/DocumentMoreSetting.tsx:234 +#: components/Document/DocumentMoreSetting.tsx:235 #: components/Document/LeftPanel/LessonItem.tsx:112 msgid "Duplicate" msgstr "Duplicate" @@ -580,7 +596,7 @@ msgstr "Duplicate" #~ msgid "Duplicate to class" #~ msgstr "Duplicate to class" -#: components/Document/DocumentMoreSetting.tsx:59 +#: components/Document/DocumentMoreSetting.tsx:60 #: components/Document/LeftPanel/LessonItem.tsx:69 msgid "Duplicated!" msgstr "Duplicated!" @@ -593,7 +609,8 @@ msgstr "Duplicated!" #~ msgid "Edit category" #~ msgstr "Edit category" -#: components/Document/DocumentSetting.tsx:201 +#: components/Document/DocumentSetting.tsx:196 +#: components/DocumentV2/DocumentBody/CoverPage/CoverPhotoHeader.tsx:84 msgid "Edit Cover" msgstr "Edit Cover" @@ -619,7 +636,7 @@ msgstr "Edit score" #: components/IdentityInput/index.tsx:62 #: components/MagicLink/index.tsx:44 -#: components/PreJoinSpace.tsx:50 +#: components/PreJoinSpace.tsx:57 #: components/UserCredential/EditProfileModal.tsx:47 msgid "Email" msgstr "Email" @@ -674,7 +691,7 @@ msgstr "Exit Full Screen" msgid "Expire" msgstr "Expire" -#: components/SpaceSetting/CreateSpaceInvite.tsx:57 +#: components/SpaceSetting/CreateSpaceInvite.tsx:60 msgid "Expire after" msgstr "Expire after" @@ -711,7 +728,7 @@ msgstr "Failed! Please try again." msgid "feedback" msgstr "feedback" -#: components/Document/DocumentHeader.tsx:293 +#: components/Document/DocumentHeader.tsx:306 msgid "Feedback" msgstr "Feedback" @@ -735,15 +752,19 @@ msgstr "First name" #~ msgid "First Name" #~ msgstr "First Name" -#: components/Document/DocumentHeader.tsx:278 +#: components/Document/DocumentHeader.tsx:291 msgid "Focus Mode" msgstr "Focus Mode" +#: components/common/LearningModuleDnd/CreateContentButton.tsx:94 +msgid "Folder" +msgstr "Folder" + #: components/UserCredential/SignIn.tsx:123 #~ msgid "Forgot password" #~ msgstr "Forgot password" -#: components/Document/AssignmentSetting/AssignmentGradeSettingTab/Rubric.tsx:307 +#: components/Document/AssignmentSetting/AssignmentGradeSettingTab/Rubric.tsx:306 msgid "From point" msgstr "From point" @@ -751,11 +772,11 @@ msgstr "From point" msgid "Full Screen" msgstr "Full Screen" -#: components/SpaceSetting/index.tsx:23 +#: components/SpaceSetting/index.tsx:31 msgid "General" msgstr "General" -#: components/SpaceSetting/CreateSpaceInvite.tsx:92 +#: components/SpaceSetting/CreateSpaceInvite.tsx:95 msgid "Generate" msgstr "Generate" @@ -766,7 +787,7 @@ msgstr "Generate Invite Link" #: components/Document/AssignmentSetting/index.tsx:38 #: components/Document/AssignmentStudentList.tsx:180 #: components/Document/AssignmentStudentList.tsx:214 -#: components/Document/DocumentHeader.tsx:322 +#: components/Document/DocumentHeader.tsx:335 #: components/Document/RightPanel/GradeSection.tsx:87 #: components/Document/RightPanel/GradeSection.tsx:122 msgid "Grade" @@ -789,6 +810,18 @@ msgstr "Hour : Minute : Second" #~ msgid "How about class title?" #~ msgstr "How about class title?" +#: components/Document/AssignmentSetting/index.tsx:184 +msgid "ikigai" +msgstr "ikigai" + +#: components/Document/AssignmentSetting/AssignmentGradeSettingTab/index.tsx:157 +msgid "ikigai will auto calculate score and release to student." +msgstr "ikigai will auto calculate score and release to student." + +#: components/Document/AssignmentSetting/AssignmentGradeSettingTab/index.tsx:151 +msgid "ikigai will try to pre-calculate score of submissions. But teacher need to review submission and feedback to student." +msgstr "ikigai will try to pre-calculate score of submissions. But teacher need to review submission and feedback to student." + #: components/Document/AssignmentStudentList.tsx:39 msgid "In Doing" msgstr "In Doing" @@ -809,26 +842,34 @@ msgstr "Invite token" msgid "Inviter" msgstr "Inviter" -#: components/SpaceSetting/index.tsx:33 +#: components/SpaceSetting/index.tsx:45 msgid "Invites" msgstr "Invites" -#: components/SpaceSetting/CreateSpaceInvite.tsx:73 +#: components/SpaceSetting/CreateSpaceInvite.tsx:76 #: components/SpaceSetting/InviteSpaceSetting.tsx:72 msgid "Inviting role" msgstr "Inviting role" #: components/Trash/index.tsx:18 -msgid "Item are available for 60 days. After that time, item will be permanently deleted." -msgstr "Item are available for 60 days. After that time, item will be permanently deleted." +#~ msgid "Item are available for 60 days. After that time, item will be permanently deleted." +#~ msgstr "Item are available for 60 days. After that time, item will be permanently deleted." -#: components/PreJoinSpace.tsx:66 +#: components/PreJoinSpace.tsx:73 msgid "Join" msgstr "Join" +#: components/PreJoinSpace.tsx:80 +msgid "Joined!" +msgstr "Joined!" + +#: components/PreJoinSpace.tsx:43 +msgid "Joined! Please check your mail inbox" +msgstr "Joined! Please check your mail inbox" + #: components/PreJoinSpace.tsx:40 -msgid "Joined! We're moving you to your space..." -msgstr "Joined! We're moving you to your space..." +#~ msgid "Joined! We're moving you to your space..." +#~ msgstr "Joined! We're moving you to your space..." #: components/UserCredential/EditProfileModal.tsx:63 msgid "Last name" @@ -875,11 +916,11 @@ msgstr "Limit the number of times to take the assignment" msgid "Manual" msgstr "Manual" -#: components/SpaceSetting/MemberSpaceSetting.tsx:34 +#: components/SpaceSetting/MemberSpaceSetting.tsx:35 msgid "Member since" msgstr "Member since" -#: components/SpaceSetting/index.tsx:28 +#: components/SpaceSetting/index.tsx:40 msgid "Members" msgstr "Members" @@ -896,7 +937,7 @@ msgstr "Method" msgid "minutes" msgstr "minutes" -#: components/Document/DocumentMoreSetting.tsx:160 +#: components/Document/DocumentMoreSetting.tsx:161 msgid "Mono" msgstr "Mono" @@ -916,6 +957,7 @@ msgstr "Multiple Choice" #~ msgid "My Profile" #~ msgstr "My Profile" +#: components/Document/DocumentAssignedList.tsx:101 #: components/SpaceSetting/MemberSpaceSetting.tsx:16 msgid "Name" msgstr "Name" @@ -971,19 +1013,7 @@ msgstr "OK" msgid "Oops! We found a error." msgstr "Oops! We found a error." -#: components/Document/AssignmentSetting/index.tsx:184 -msgid "ikigai" -msgstr "ikigai" - -#: components/Document/AssignmentSetting/AssignmentGradeSettingTab/index.tsx:157 -msgid "ikigai will auto calculate score and release to student." -msgstr "ikigai will auto calculate score and release to student." - -#: components/Document/AssignmentSetting/AssignmentGradeSettingTab/index.tsx:151 -msgid "ikigai will try to pre-calculate score of submissions. But teacher need to review submission and feedback to student." -msgstr "ikigai will try to pre-calculate score of submissions. But teacher need to review submission and feedback to student." - -#: components/common/RichMarkdownEditor/extensions/QuizExtension/QuizSetting/QuizDropdownMenu.tsx:130 +#: components/common/RichMarkdownEditor/extensions/QuizExtension/QuizSetting/QuizDropdownMenu.tsx:129 msgid "Open settings" msgstr "Open settings" @@ -1060,9 +1090,9 @@ msgstr "Please choose the correct answer and save again!" #~ msgid "Please enter your password" #~ msgstr "Please enter your password" -#: components/Document/AssignmentSetting/AssignmentGradeSettingTab/Rubric.tsx:276 -#: components/Document/AssignmentSetting/AssignmentGradeSettingTab/Rubric.tsx:298 -#: components/Document/AssignmentSetting/AssignmentGradeSettingTab/Rubric.tsx:313 +#: components/Document/AssignmentSetting/AssignmentGradeSettingTab/Rubric.tsx:275 +#: components/Document/AssignmentSetting/AssignmentGradeSettingTab/Rubric.tsx:297 +#: components/Document/AssignmentSetting/AssignmentGradeSettingTab/Rubric.tsx:312 msgid "point" msgstr "point" @@ -1106,7 +1136,7 @@ msgstr "Question" msgid "questions" msgstr "questions" -#: components/Document/DocumentHeader.tsx:322 +#: components/Document/DocumentHeader.tsx:335 msgid "Questions" msgstr "Questions" @@ -1119,8 +1149,8 @@ msgid "Quiz Settings" msgstr "Quiz Settings" #: components/Trash/index.tsx:14 -msgid "Recently Deleted" -msgstr "Recently Deleted" +#~ msgid "Recently Deleted" +#~ msgstr "Recently Deleted" #: components/common/RichMarkdownEditor/extensions/RecordExtension/RecordBlock.tsx:132 msgid "Recording Failed" @@ -1134,7 +1164,12 @@ msgstr "Reload" msgid "Remaining" msgstr "Remaining" -#: components/Document/DocumentSetting.tsx:146 +#: components/Document/DocumentAssignedList.tsx:118 +msgid "Remove assigned user" +msgstr "Remove assigned user" + +#: components/Document/DocumentSetting.tsx:141 +#: components/DocumentV2/DocumentBody/CoverPage/CoverPhotoHeader.tsx:55 msgid "Remove Cover" msgstr "Remove Cover" @@ -1179,15 +1214,14 @@ msgstr "Reset" #~ msgid "Reset your password" #~ msgstr "Reset your password" -#: components/Document/DocumentSetting.tsx:178 -#: components/Trash/DeletedItem.tsx:97 +#: components/Document/DocumentSetting.tsx:173 msgid "Restore" msgstr "Restore" #: components/Trash/DeletedDocumentList.tsx:34 #: components/Trash/DeletedSpaceList.tsx:34 -msgid "Restored!" -msgstr "Restored!" +#~ msgid "Restored!" +#~ msgstr "Restored!" #: components/DocumentVersionHistory/DocumentHistoryList.tsx:64 #~ msgid "Restoring" @@ -1201,11 +1235,11 @@ msgstr "Review & Grade" msgid "Review last submission" msgstr "Review last submission" -#: components/Document/DocumentHeader.tsx:308 +#: components/Document/DocumentHeader.tsx:321 msgid "Review setting quizzes" msgstr "Review setting quizzes" -#: components/SpaceSetting/MemberSpaceSetting.tsx:28 +#: components/SpaceSetting/MemberSpaceSetting.tsx:29 msgid "Role" msgstr "Role" @@ -1213,7 +1247,7 @@ msgstr "Role" msgid "Rubric" msgstr "Rubric" -#: components/Document/RightPanel/RubricSection.tsx:122 +#: components/Document/RightPanel/RubricSection.tsx:120 msgid "RUBRIC" msgstr "RUBRIC" @@ -1221,21 +1255,21 @@ msgstr "RUBRIC" msgid "Rubric Config" msgstr "Rubric Config" -#: components/Document/AssignmentSetting/AssignmentGradeSettingTab/RubricManagement.tsx:99 +#: components/Document/AssignmentSetting/AssignmentGradeSettingTab/RubricManagement.tsx:96 msgid "Rubric Management" msgstr "Rubric Management" -#: components/Document/AssignmentSetting/AssignmentGradeSettingTab/Rubric.tsx:406 +#: components/Document/AssignmentSetting/AssignmentGradeSettingTab/Rubric.tsx:405 msgid "Rubric Type" msgstr "Rubric Type" -#: components/Document/AssignmentSetting/AssignmentGradeSettingTab/RubricManagement.tsx:106 +#: components/Document/AssignmentSetting/AssignmentGradeSettingTab/RubricManagement.tsx:103 msgid "Rubrics consist of rows and columns. The rows correspond to the criteria. The columns correspond to the level of achievement that describes each criterion." msgstr "Rubrics consist of rows and columns. The rows correspond to the criteria. The columns correspond to the level of achievement that describes each criterion." #: components/common/RichMarkdownEditor/extensions/QuizExtension/FillInBlank/FillInBlankSetting.tsx:148 #: components/common/RichMarkdownEditor/extensions/QuizExtension/QuizSetting/index.tsx:152 -#: components/Document/AssignmentSetting/AssignmentGradeSettingTab/Rubric.tsx:400 +#: components/Document/AssignmentSetting/AssignmentGradeSettingTab/Rubric.tsx:399 #: components/Document/AssignmentSetting/index.tsx:247 msgid "Save" msgstr "Save" @@ -1252,7 +1286,7 @@ msgstr "Save" #~ msgid "Saved!" #~ msgstr "Saved!" -#: components/Document/DocumentHeader.tsx:152 +#: components/Document/DocumentHeader.tsx:159 msgid "Saving" msgstr "Saving" @@ -1270,7 +1304,7 @@ msgid "Select a bandscore" msgstr "Select a bandscore" #: components/common/RichMarkdownEditor/extensions/PageBlockExtension/PageBlock.tsx:283 -#: components/common/RichMarkdownEditor/extensions/QuizExtension/QuizSetting/QuizDropdownMenu.tsx:121 +#: components/common/RichMarkdownEditor/extensions/QuizExtension/QuizSetting/QuizDropdownMenu.tsx:120 msgid "Select and copy" msgstr "Select and copy" @@ -1294,7 +1328,11 @@ msgstr "Send me a magic link!" msgid "Sent. Please check your email inbox!" msgstr "Sent. Please check your email inbox!" -#: components/Document/DocumentMoreSetting.tsx:148 +#: components/Document/DocumentAssignedList.tsx:142 +msgid "Seperated by commas or space" +msgstr "Seperated by commas or space" + +#: components/Document/DocumentMoreSetting.tsx:149 msgid "Serif" msgstr "Serif" @@ -1302,7 +1340,7 @@ msgstr "Serif" msgid "Setting quizzes review" msgstr "Setting quizzes review" -#: components/Document/DocumentHeader.tsx:239 +#: components/Document/DocumentHeader.tsx:252 msgid "Settings" msgstr "Settings" @@ -1310,7 +1348,7 @@ msgstr "Settings" msgid "Setup the rubric for this assignment. Teacher will grade submissions by following the rubric criteria." msgstr "Setup the rubric for this assignment. Teacher will grade submissions by following the rubric criteria." -#: components/Document/DocumentHeader.tsx:264 +#: components/Document/DocumentHeader.tsx:277 msgid "Share" msgstr "Share" @@ -1318,7 +1356,7 @@ msgstr "Share" msgid "Single Choice" msgstr "Single Choice" -#: components/Document/DocumentMoreSetting.tsx:167 +#: components/Document/DocumentMoreSetting.tsx:168 msgid "Size" msgstr "Size" @@ -1326,7 +1364,7 @@ msgstr "Size" #~ msgid "Space" #~ msgstr "Space" -#: components/SpaceSetting/CreateSpaceInvite.tsx:52 +#: components/SpaceSetting/CreateSpaceInvite.tsx:56 msgid "Space invite link" msgstr "Space invite link" @@ -1334,7 +1372,7 @@ msgstr "Space invite link" msgid "Space name" msgstr "Space name" -#: components/SpaceSetting/index.tsx:43 +#: components/SpaceSetting/index.tsx:56 msgid "Space settings" msgstr "Space settings" @@ -1375,7 +1413,7 @@ msgstr "Student will follow setup of teacher." #~ msgid "Students can access." #~ msgstr "Students can access." -#: components/Document/DocumentMoreSetting.tsx:124 +#: components/Document/DocumentMoreSetting.tsx:125 msgid "Style" msgstr "Style" @@ -1383,14 +1421,14 @@ msgstr "Style" msgid "Styles" msgstr "Styles" -#: components/Document/Assignment/AssignmentHeader.tsx:41 +#: components/Document/Assignment/AssignmentHeader.tsx:54 #: components/Document/AssignmentStudentList.tsx:251 msgid "Submission List" msgstr "Submission List" #: components/Document/Submission/DoingSubmissionDocument.tsx:95 -msgid "Submit" -msgstr "Submit" +#~ msgid "Submit" +#~ msgstr "Submit" #: components/Document/AssignmentStudentList.tsx:122 #: components/Document/RightPanel/SummarySection.tsx:78 @@ -1401,7 +1439,7 @@ msgstr "Submit at" msgid "Submitted" msgstr "Submitted" -#: components/Document/AssignmentSetting/AssignmentGradeSettingTab/Rubric.tsx:128 +#: components/Document/AssignmentSetting/AssignmentGradeSettingTab/Rubric.tsx:127 msgid "Successfully!" msgstr "Successfully!" @@ -1418,7 +1456,7 @@ msgstr "Tag" #~ msgid "Tags" #~ msgstr "Tags" -#: components/SpaceSetting/CreateSpaceInvite.tsx:82 +#: components/SpaceSetting/CreateSpaceInvite.tsx:84 msgid "Teacher" msgstr "Teacher" @@ -1450,7 +1488,7 @@ msgstr "The option that accepts input with sensitive (uppercase and lowercase) c msgid "There are some problems with this document. {fetchError}" msgstr "There are some problems with this document. {fetchError}" -#: components/Document/DocumentMoreSetting.tsx:70 +#: components/Document/DocumentMoreSetting.tsx:71 #: components/Document/LeftPanel/LessonItem.tsx:126 msgid "" "This action cannot be undone. Once you delete this, \n" @@ -1467,7 +1505,7 @@ msgstr "This assignment will be counted into final grade." msgid "This assignment will not be counted into final grade." msgstr "This assignment will not be counted into final grade." -#: components/Document/DocumentSetting.tsx:171 +#: components/Document/DocumentSetting.tsx:166 msgid "This document is in the trash." msgstr "This document is in the trash." @@ -1475,7 +1513,7 @@ msgstr "This document is in the trash." msgid "Time complete" msgstr "Time complete" -#: components/Document/AssignmentSetting/AssignmentGradeSettingTab/Rubric.tsx:322 +#: components/Document/AssignmentSetting/AssignmentGradeSettingTab/Rubric.tsx:321 msgid "To point" msgstr "To point" @@ -1491,7 +1529,7 @@ msgstr "" msgid "True, False, Not Given" msgstr "True, False, Not Given" -#: components/Document/DocumentMoreSetting.tsx:99 +#: components/Document/DocumentMoreSetting.tsx:100 msgid "Try again!" msgstr "Try again!" @@ -1546,7 +1584,7 @@ msgid "Type your email" msgstr "Type your email" #: components/MagicLink/index.tsx:47 -#: components/PreJoinSpace.tsx:53 +#: components/PreJoinSpace.tsx:60 msgid "Type your email!" msgstr "Type your email!" @@ -1558,11 +1596,11 @@ msgstr "Type your feedback" #~ msgid "Typing category name" #~ msgstr "Typing category name" -#: components/Document/AssignmentSetting/AssignmentGradeSettingTab/Rubric.tsx:230 +#: components/Document/AssignmentSetting/AssignmentGradeSettingTab/Rubric.tsx:229 msgid "Typing criteria" msgstr "Typing criteria" -#: components/Document/AssignmentSetting/AssignmentGradeSettingTab/Rubric.tsx:244 +#: components/Document/AssignmentSetting/AssignmentGradeSettingTab/Rubric.tsx:243 msgid "Typing level" msgstr "Typing level" @@ -1570,7 +1608,7 @@ msgstr "Typing level" #~ msgid "Typing template name" #~ msgstr "Typing template name" -#: components/Document/AssignmentSetting/AssignmentGradeSettingTab/Rubric.tsx:335 +#: components/Document/AssignmentSetting/AssignmentGradeSettingTab/Rubric.tsx:334 msgid "Typing your description" msgstr "Typing your description" @@ -1578,7 +1616,8 @@ msgstr "Typing your description" msgid "Ungraded" msgstr "Ungraded" -#: components/Document/LeftPanel/index.tsx:40 +#: components/Document/LeftPanel/index.tsx:44 +#: components/DocumentV2/LeftSide/index.tsx:31 msgid "Unknown" msgstr "Unknown" @@ -1591,7 +1630,8 @@ msgid "Unlimited time" msgstr "Unlimited time" #: components/Document/Assignment/ReviewAssignmentDocument.tsx:154 -#: components/Document/DocumentSetting.tsx:237 +#: components/Document/DocumentSetting.tsx:232 +#: components/DocumentV2/DocumentBody/CoverPage/index.tsx:59 msgid "Untitled" msgstr "Untitled" @@ -1617,11 +1657,12 @@ msgstr "Updated!" msgid "Upload" msgstr "Upload" -#: components/Document/DocumentSetting.tsx:138 +#: components/Document/DocumentSetting.tsx:133 +#: components/DocumentV2/DocumentBody/CoverPage/CoverPhotoHeader.tsx:46 msgid "Upload Cover" msgstr "Upload Cover" -#: components/common/FileUpload.tsx:91 +#: components/common/FileUpload.tsx:93 msgid "upload failed." msgstr "upload failed." @@ -1633,7 +1674,7 @@ msgstr "Upload File" msgid "Upload Media" msgstr "Upload Media" -#: components/common/FileUpload.tsx:88 +#: components/common/FileUpload.tsx:90 msgid "uploaded successfully." msgstr "uploaded successfully." @@ -1645,7 +1686,7 @@ msgstr "Uploaded!" #~ msgid "Use template" #~ msgstr "Use template" -#: components/Document/AssignmentSetting/AssignmentGradeSettingTab/RubricManagement.tsx:149 +#: components/Document/AssignmentSetting/AssignmentGradeSettingTab/RubricManagement.tsx:146 msgid "Use this rubric" msgstr "Use this rubric" @@ -1658,11 +1699,11 @@ msgid "Uses" msgstr "Uses" #: components/common/PanelDetail.tsx:42 -#: components/Document/AssignmentSetting/AssignmentGradeSettingTab/RubricManagement.tsx:160 +#: components/Document/AssignmentSetting/AssignmentGradeSettingTab/RubricManagement.tsx:157 msgid "View" msgstr "View" -#: components/Document/AssignmentSetting/AssignmentGradeSettingTab/RubricManagement.tsx:159 +#: components/Document/AssignmentSetting/AssignmentGradeSettingTab/RubricManagement.tsx:156 msgid "View and Edit" msgstr "View and Edit" @@ -1691,12 +1732,16 @@ msgstr "We're developing it!!" #~ msgid "welcome" #~ msgstr "welcome" -#: components/Document/DocumentMoreSetting.tsx:194 +#: components/Document/DocumentAssignedList.tsx:133 +msgid "When a person is assigned to this document, they will receive receive an email granting access to the document and enrolling them as a space student." +msgstr "When a person is assigned to this document, they will receive receive an email granting access to the document and enrolling them as a space student." + +#: components/Document/DocumentMoreSetting.tsx:195 msgid "Width" msgstr "Width" #: components/MagicLink/index.tsx:24 -#: components/PreJoinSpace.tsx:25 +#: components/PreJoinSpace.tsx:29 msgid "Wrong email format!" msgstr "Wrong email format!" @@ -1709,20 +1754,24 @@ msgid "Yes, No, Not Given" msgstr "Yes, No, Not Given" #: components/Trash/DeletedItem.tsx:55 -msgid "You cannot recover data if you confirm to delete." -msgstr "You cannot recover data if you confirm to delete." +#~ msgid "You cannot recover data if you confirm to delete." +#~ msgstr "You cannot recover data if you confirm to delete." #: components/EmptyState.tsx:33 msgid "You have no class here" msgstr "You have no class here" -#: components/PreJoinSpace.tsx:47 +#: components/PreJoinSpace.tsx:54 msgid "You've been invited to join ikigai" msgstr "You've been invited to join ikigai" +#: components/PreJoinSpace.tsx:83 +msgid "You've joined. We've sent a magic email to <0>{email}. Open the link in email to access space." +msgstr "You've joined. We've sent a magic email to <0>{email}. Open the link in email to access space." + #: components/Document/Submission/DoingSubmissionDocument.tsx:94 -msgid "Your submission is completed. We will move you to assignment page!" -msgstr "Your submission is completed. We will move you to assignment page!" +#~ msgid "Your submission is completed. We will move you to assignment page!" +#~ msgstr "Your submission is completed. We will move you to assignment page!" #: components/common/RichMarkdownEditor/extensions/FileExtension/FileBlockHeader.tsx:118 msgid "ZoomIn" diff --git a/apps/ikigai/storage/TokenStorage.ts b/apps/ikigai/storage/TokenStorage.ts index ffa02d0a..5c93a61d 100644 --- a/apps/ikigai/storage/TokenStorage.ts +++ b/apps/ikigai/storage/TokenStorage.ts @@ -1,5 +1,5 @@ class TokenStorage { - key: string = "openexam_token"; + key: string = "ikigai_token"; public get(): string | null { if (typeof window === "undefined") { diff --git a/graphql_server/src/db/notification.rs b/graphql_server/src/db/notification.rs index 4d16a9b3..cafc5be6 100644 --- a/graphql_server/src/db/notification.rs +++ b/graphql_server/src/db/notification.rs @@ -6,10 +6,10 @@ use uuid::Uuid; use super::schema::{notification_receivers, notifications}; use crate::db::User; +use crate::helper::generate_magic_link; use crate::impl_enum_for_db; -use crate::service::redis::Redis; -use crate::util::url_util::{document_url, magic_link_for_document_url, space_url}; -use crate::util::{generate_otp, get_now_as_secs}; +use crate::util::get_now_as_secs; +use crate::util::url_util::{format_document_url, format_space_url}; #[derive( Debug, Clone, Copy, Eq, PartialEq, FromPrimitive, ToPrimitive, AsExpression, FromSqlRow, Enum, @@ -104,7 +104,7 @@ impl ContextMessage for NewSpaceMemberContext { } fn get_url_path(&self, _: &User) -> String { - space_url(self.space_id) + format_space_url(self.space_id) } } @@ -130,7 +130,7 @@ impl ContextMessage for SubmitSubmissionContext { } fn get_url_path(&self, _: &User) -> String { - document_url(self.document_submission_id) + format_document_url(self.document_submission_id) } } @@ -155,7 +155,7 @@ Great news! Your teacher has provided feedback on your submission in {submission } fn get_url_path(&self, _: &User) -> String { - document_url(self.document_submission_id) + format_document_url(self.document_submission_id) } } @@ -179,12 +179,8 @@ Hello there! You've been assigned to a new assignment: {assignment_name}. If you } fn get_url_path(&self, receiver: &User) -> String { - let otp = generate_otp(); - if Redis::init().set_magic_token(receiver.id, &otp).is_ok() { - magic_link_for_document_url(self.assignment_document_id, &otp, receiver.id) - } else { - document_url(self.assignment_document_id) - } + generate_magic_link(receiver.id, self.assignment_document_id) + .unwrap_or(format_document_url(self.assignment_document_id)) } } diff --git a/graphql_server/src/graphql/space_action/space_mutation.rs b/graphql_server/src/graphql/space_action/space_mutation.rs index de32d6e7..afe6887c 100644 --- a/graphql_server/src/graphql/space_action/space_mutation.rs +++ b/graphql_server/src/graphql/space_action/space_mutation.rs @@ -2,7 +2,6 @@ use async_graphql::*; use diesel::Connection; use uuid::Uuid; -use crate::authentication_token::Claims; use crate::authorization::{DocumentActionPermission, SpaceActionPermission}; use crate::db::*; use crate::error::{IkigaiError, IkigaiErrorExt}; @@ -10,12 +9,6 @@ use crate::helper::*; use crate::notification_center::send_notification; use crate::util::get_now_as_secs; -#[derive(SimpleObject)] -pub struct SpaceWithAccessToken { - pub starter_document: Document, - pub access_token: String, -} - #[derive(Default)] pub struct SpaceMutation; @@ -220,7 +213,7 @@ impl SpaceMutation { email: String, space_id: i32, token: String, - ) -> Result { + ) -> Result { let conn = get_conn_from_ctx(ctx).await?; let space = Space::find_by_id(&conn, space_id).format_err()?; let space_invite_token = SpaceInviteToken::find(&conn, space_id, &token).format_err()?; @@ -243,33 +236,38 @@ impl SpaceMutation { User::insert(&conn, &new_user).format_err()? }; - SpaceInviteToken::increase_use(&conn, space.id, &token).format_err()?; - let space_member = add_space_member( - &conn, - &space, - user.id, - Some(token), - space_invite_token.inviting_role, - ) - .format_err()?; - let claims = Claims::new(space_member.user_id); - let access_token = claims.encode()?; + let current_space_member = SpaceMember::find(&conn, space.id, user.id); + let is_new_space_member = current_space_member.is_err(); + let space_member = if let Ok(current_space_member) = current_space_member { + current_space_member + } else { + SpaceInviteToken::increase_use(&conn, space.id, &token).format_err()?; + add_space_member( + &conn, + &space, + user.id, + Some(token), + space_invite_token.inviting_role, + ) + .format_err()? + }; let starter_document = Document::get_or_create_starter_doc(&conn, space_member.user_id, space_id) .format_err()?; + let res = send_space_magic_link(&user, starter_document.id); + + // Notify space owner + if is_new_space_member { + let notification = Notification::new_space_member_notification(NewSpaceMemberContext { + space_name: space.name, + space_id: space.id, + email: user.email, + }); + let notification = Notification::insert(&conn, notification).format_err()?; + send_notification(&conn, notification, vec![space.creator_id]).format_err()?; + } - let notification = Notification::new_space_member_notification(NewSpaceMemberContext { - space_name: space.name, - space_id: space.id, - email: user.email, - }); - let notification = Notification::insert(&conn, notification).format_err()?; - send_notification(&conn, notification, vec![space.creator_id]).format_err()?; - - Ok(SpaceWithAccessToken { - starter_document, - access_token, - }) + res } async fn space_remove_invite_token( diff --git a/graphql_server/src/graphql/user_action/user_mutation.rs b/graphql_server/src/graphql/user_action/user_mutation.rs index cc9e7d05..d9d226f1 100644 --- a/graphql_server/src/graphql/user_action/user_mutation.rs +++ b/graphql_server/src/graphql/user_action/user_mutation.rs @@ -9,11 +9,10 @@ use crate::error::{IkigaiError, IkigaiErrorExt}; use crate::graphql::validator::Email; use crate::helper::{ get_conn_from_ctx, get_user_from_ctx, get_user_id_from_ctx, rubric_quick_authorize, + send_space_magic_link, }; -use crate::mailer::Mailer; use crate::service::redis::Redis; -use crate::util::url_util::magic_link_for_document_url; -use crate::util::{generate_otp, get_now_as_secs}; +use crate::util::get_now_as_secs; #[derive(SimpleObject)] pub struct UserToken { @@ -89,15 +88,7 @@ impl UserMutation { } }; - let otp = generate_otp(); - Redis::init().set_magic_token(user.id, &otp).format_err()?; - let magic_link = magic_link_for_document_url(document.id, &otp, user.id); - if let Err(reason) = Mailer::send_magic_link_email(&user.email, magic_link) { - error!("Cannot send magic link to {}: {:?}", user.email, reason); - Ok(false) - } else { - Ok(true) - } + send_space_magic_link(&user, document.id) } async fn user_check_magic_link( diff --git a/graphql_server/src/helper/authorize_helper.rs b/graphql_server/src/helper/authorize_helper.rs index 76d9422b..2b2aede4 100644 --- a/graphql_server/src/helper/authorize_helper.rs +++ b/graphql_server/src/helper/authorize_helper.rs @@ -70,7 +70,7 @@ pub async fn get_user_auth_by_user_id_from_ctx( } else { let space_id = get_active_space_id_from_ctx(ctx).await?; let conn = get_conn_from_ctx(ctx).await?; - let space_member = SpaceMember::find(&conn, user_id, space_id).format_err()?; + let space_member = SpaceMember::find(&conn, space_id, user_id).format_err()?; let user_auth = UserAuth::new(space_member); info!("Set cache user auth {:?}", user_auth); @@ -236,6 +236,7 @@ pub async fn get_document_allowed_permissions( let conn = get_conn_from_ctx(ctx).await?; let document_auth = DocumentAuth::try_new(&conn, document_id).format_err()?; + info!("Hello {} {:?}", document_id, user_auth); let oso = ctx.data::()?; let actions: HashSet = oso.get_allowed_actions(user_auth, document_auth)?; Ok(actions diff --git a/graphql_server/src/helper/mod.rs b/graphql_server/src/helper/mod.rs index f766e995..e7dd28ff 100644 --- a/graphql_server/src/helper/mod.rs +++ b/graphql_server/src/helper/mod.rs @@ -9,12 +9,16 @@ pub use submission_helper::*; use async_graphql::dataloader::DataLoader; use async_graphql::*; use diesel::{Connection as DieselConnection, PgConnection}; +use uuid::Uuid; use crate::db::*; use crate::error::{IkigaiError, IkigaiErrorExt}; use crate::graphql::data_loader::{FindPublicUserById, IkigaiDataLoader}; +use crate::mailer::Mailer; +use crate::service::redis::Redis; use crate::service::Storage; -use crate::util::get_now_as_secs; +use crate::util::url_util::format_magic_link; +use crate::util::{generate_otp, get_now_as_secs}; pub async fn get_public_user_from_loader(ctx: &Context<'_>, user_id: i32) -> Result { let loader = ctx.data_unchecked::>(); @@ -110,3 +114,19 @@ pub fn add_space_member( Ok(new_member) } + +pub fn generate_magic_link(user_id: i32, document_id: Uuid) -> Result { + let otp = generate_otp(); + Redis::init().set_magic_token(user_id, &otp)?; + Ok(format_magic_link(document_id, &otp, user_id)) +} + +pub fn send_space_magic_link(user: &User, document_id: Uuid) -> Result { + let magic_link = generate_magic_link(user.id, document_id).format_err()?; + if let Err(reason) = Mailer::send_magic_link_email(&user.email, magic_link) { + error!("Cannot send magic link to {}: {:?}", user.email, reason); + Ok(false) + } else { + Ok(true) + } +} diff --git a/graphql_server/src/util/url_util.rs b/graphql_server/src/util/url_util.rs index d64e2001..a196826a 100644 --- a/graphql_server/src/util/url_util.rs +++ b/graphql_server/src/util/url_util.rs @@ -4,17 +4,17 @@ pub fn get_base_url() -> String { std::env::var("APP_URL").unwrap_or("http://localhost:3000".to_string()) } -pub fn magic_link_for_document_url(document_id: Uuid, otp: &str, user_id: i32) -> String { +pub fn format_magic_link(document_id: Uuid, otp: &str, user_id: i32) -> String { let base_url = get_base_url(); format!("{base_url}/documents/{document_id}?otp={otp}&user_id={user_id}") } -pub fn document_url(document_id: Uuid) -> String { +pub fn format_document_url(document_id: Uuid) -> String { let base_url = get_base_url(); format!("{base_url}/documents/{document_id}") } -pub fn space_url(space_id: i32) -> String { +pub fn format_space_url(space_id: i32) -> String { let base_url = get_base_url(); format!("{base_url}/spaces/{space_id}") }