diff --git a/.github/workflows/docker-build-publish-dev.yml b/.github/workflows/docker-build-publish-dev.yml index fd06f4efe..dd8e5fcf0 100644 --- a/.github/workflows/docker-build-publish-dev.yml +++ b/.github/workflows/docker-build-publish-dev.yml @@ -98,6 +98,6 @@ jobs: username: ${{ secrets.CW_DOCKER_USER }} password: ${{ secrets.CW_DOCKER_USER_PASSWORD }} - - name: Push to CW Registry - run: | - docker push ${{ secrets.CW_DOCKER_REGISTRY }}/ever-co/ever-teams-webapp-dev:latest + # - name: Push to CW Registry + # run: | + # docker push ${{ secrets.CW_DOCKER_REGISTRY }}/ever-co/ever-teams-webapp-dev:latest diff --git a/.github/workflows/docker-build-publish-prod.yml b/.github/workflows/docker-build-publish-prod.yml index b62a74490..929e1d8cc 100644 --- a/.github/workflows/docker-build-publish-prod.yml +++ b/.github/workflows/docker-build-publish-prod.yml @@ -98,6 +98,6 @@ jobs: username: ${{ secrets.CW_DOCKER_USER }} password: ${{ secrets.CW_DOCKER_USER_PASSWORD }} - - name: Push to CW Registry - run: | - docker push ${{ secrets.CW_DOCKER_REGISTRY }}/ever-co/ever-teams-webapp:latest + # - name: Push to CW Registry + # run: | + # docker push ${{ secrets.CW_DOCKER_REGISTRY }}/ever-co/ever-teams-webapp:latest diff --git a/.github/workflows/docker-build-publish-stage.yml b/.github/workflows/docker-build-publish-stage.yml index 080077d95..32d2d3800 100644 --- a/.github/workflows/docker-build-publish-stage.yml +++ b/.github/workflows/docker-build-publish-stage.yml @@ -98,6 +98,6 @@ jobs: username: ${{ secrets.CW_DOCKER_USER }} password: ${{ secrets.CW_DOCKER_USER_PASSWORD }} - - name: Push to CW Registry - run: | - docker push ${{ secrets.CW_DOCKER_REGISTRY }}/ever-co/ever-teams-webapp-stage:latest + # - name: Push to CW Registry + # run: | + # docker push ${{ secrets.CW_DOCKER_REGISTRY }}/ever-co/ever-teams-webapp-stage:latest diff --git a/apps/web/app/[locale]/auth/passcode/component.tsx b/apps/web/app/[locale]/auth/passcode/component.tsx index 0b52e1204..5cc5cb0ad 100644 --- a/apps/web/app/[locale]/auth/passcode/component.tsx +++ b/apps/web/app/[locale]/auth/passcode/component.tsx @@ -186,7 +186,7 @@ function PasscodeScreen({ form, className }: { form: TAuthenticationPasscode } & if (formRef.current) { formRef.current.dispatchEvent(new Event('submit', { cancelable: true, bubbles: true })); } - } + }; return (
@@ -306,6 +306,7 @@ function WorkSpaceScreen({ form, className }: { form: TAuthenticationPasscode } (e: any) => { if (typeof selectedWorkspace !== 'undefined') { form.handleWorkspaceSubmit(e, form.workspaces[selectedWorkspace].token, selectedTeam); + window && window?.localStorage.removeItem('user-saw-notif'); } }, [selectedWorkspace, selectedTeam, form] @@ -387,8 +388,9 @@ export function WorkSpaceComponent(props: IWorkSpace) { {props.workspaces?.map((worksace, index) => (
diff --git a/apps/web/app/[locale]/auth/password/component.tsx b/apps/web/app/[locale]/auth/password/component.tsx index 4d87760aa..2876dd839 100644 --- a/apps/web/app/[locale]/auth/password/component.tsx +++ b/apps/web/app/[locale]/auth/password/component.tsx @@ -107,6 +107,7 @@ function WorkSpaceScreen({ form, className }: { form: TAuthenticationPassword } (e: any) => { if (typeof selectedWorkspace !== 'undefined') { form.handleWorkspaceSubmit(e, form.workspaces[selectedWorkspace].token, selectedTeam); + window && window?.localStorage.removeItem('user-saw-notif'); } }, [selectedWorkspace, selectedTeam, form] diff --git a/apps/web/app/[locale]/auth/workspace/component.tsx b/apps/web/app/[locale]/auth/workspace/component.tsx index a7f0624f5..6bc8a2778 100644 --- a/apps/web/app/[locale]/auth/workspace/component.tsx +++ b/apps/web/app/[locale]/auth/workspace/component.tsx @@ -64,6 +64,7 @@ function WorkSpaceScreen() { new Array(3).fill('').forEach((_, i) => { Cookies.remove(`authjs.session-token.${i}`); }); + window && window?.localStorage.removeItem('user-saw-notif'); }; const updateOAuthSession = useCallback(() => { diff --git a/apps/web/app/[locale]/page-component.tsx b/apps/web/app/[locale]/page-component.tsx index ffd828bf6..e1b331bf5 100644 --- a/apps/web/app/[locale]/page-component.tsx +++ b/apps/web/app/[locale]/page-component.tsx @@ -2,7 +2,7 @@ 'use client'; -import React, { useEffect } from 'react'; +import React, { useEffect, useState } from 'react'; import { useOrganizationTeams } from '@app/hooks'; import { clsxm } from '@app/utils'; import NoTeam from '@components/pages/main/no-team'; @@ -30,9 +30,13 @@ import { usePathname } from 'next/navigation'; import { PeoplesIcon } from 'assets/svg'; import TeamMemberHeader from 'lib/features/team-member-header'; import { ResizableHandle, ResizablePanel, ResizablePanelGroup } from '@components/ui/resizable'; +import { TeamOutstandingNotifications } from 'lib/features/team/team-outstanding-notifications'; function MainPage() { const t = useTranslations(); + + const [headerSize, setHeaderSize] = useState(10); + const { isTeamMember, isTrackingEnabled, activeTeam } = useOrganizationTeams(); const [fullWidth, setFullWidth] = useRecoilState(fullWidthState); const [view, setView] = useRecoilState(headerTabs); @@ -67,8 +71,16 @@ function MainPage() {
{/* */} - -
+ setHeaderSize(size)} + > +
+ {isTeamMember ? ( ) : null} diff --git a/apps/web/lib/features/team/team-outstanding-notifications.tsx b/apps/web/lib/features/team/team-outstanding-notifications.tsx new file mode 100644 index 000000000..afd691602 --- /dev/null +++ b/apps/web/lib/features/team/team-outstanding-notifications.tsx @@ -0,0 +1,95 @@ +'use client'; +import { useAuthenticateUser, useDailyPlan } from '@app/hooks'; +import { IDailyPlan, IUser } from '@app/interfaces'; +import { Cross2Icon, EyeOpenIcon } from '@radix-ui/react-icons'; +import { Tooltip } from 'lib/components'; +import { useTranslations } from 'next-intl'; +import Link from 'next/link'; +import { useEffect, useState } from 'react'; + +export function TeamOutstandingNotifications() { + const { getEmployeeDayPlans, outstandingPlans } = useDailyPlan(); + + const { user } = useAuthenticateUser(); + + useEffect(() => { + getEmployeeDayPlans(user?.employee.id || ''); + }, [getEmployeeDayPlans, user?.employee.id]); + + return ( + <> + {outstandingPlans && outstandingPlans.length > 0 && ( + + )} + + ); +} + +function UserOutstandingNotification({ outstandingTasks, user }: { outstandingTasks: IDailyPlan[]; user?: IUser }) { + const t = useTranslations(); + + // Notification will be displayed 6 hours after the user closed it + const REAPPEAR_INTERVAL = 6 * 60 * 60 * 1000; // 6 hours in milliseconds; + const DISMISSAL_TIMESTAMP_KEY = 'user-saw-notif'; + + const name = user?.name || user?.firstName || user?.lastName || user?.username; + + const [visible, setVisible] = useState(false); + const tasks = outstandingTasks.flatMap((plan) => plan.tasks); + + useEffect(() => { + const checkNotification = () => { + const alreadySeen = window && parseInt(window?.localStorage.getItem(DISMISSAL_TIMESTAMP_KEY) || '0', 10); + const currentTime = new Date().getTime(); + + if (!alreadySeen || currentTime - alreadySeen > REAPPEAR_INTERVAL) { + setVisible(true); + } + }; + + checkNotification(); + const intervalId = setInterval(checkNotification, REAPPEAR_INTERVAL); + return () => clearInterval(intervalId); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + const onClose = () => { + window && window?.localStorage.setItem(DISMISSAL_TIMESTAMP_KEY, new Date().getTime().toString()); + setVisible(false); + }; + + return ( + <> + {visible && ( +
+
+ {t('pages.home.OUTSTANDING_NOTIFICATIONS.SUBJECT')} {tasks?.length}{' '} + {t('pages.home.OUTSTANDING_NOTIFICATIONS.USER_LABEL')}{' '} + + {t('pages.home.OUTSTANDING_NOTIFICATIONS.OUTSTANDING_VIEW')} + +
+
+
+ { + onClose(); + window && window.localStorage.setItem('task-tab', 'dailyplan'); + window && window.localStorage.setItem('daily-plan-tab', 'Outstanding'); + }} + > + + {t('pages.home.OUTSTANDING_NOTIFICATIONS.VIEW_BUTTON')} + +
+ + + +
+
+ )} + + ); +} diff --git a/apps/web/lib/features/user-profile-plans.tsx b/apps/web/lib/features/user-profile-plans.tsx index 327d341e6..6e169f578 100644 --- a/apps/web/lib/features/user-profile-plans.tsx +++ b/apps/web/lib/features/user-profile-plans.tsx @@ -1,6 +1,6 @@ 'use client'; -import { useState } from 'react'; +import { useEffect, useState } from 'react'; import { useRecoilValue } from 'recoil'; import { useCanSeeActivityScreen, useDailyPlan, useUserProfilePage } from '@app/hooks'; import { TaskCard } from './task/task-card'; @@ -19,11 +19,16 @@ import { Button } from '@components/ui/button'; type FilterTabs = 'Today Tasks' | 'Future Tasks' | 'Past Tasks' | 'All Tasks' | 'Outstanding'; export function UserProfilePlans() { + const defaultTab = + typeof window !== 'undefined' + ? (window.localStorage.getItem('daily-plan-tab') as FilterTabs) || null + : 'Today Tasks'; + const profile = useUserProfilePage(); const { todayPlan, futurePlans, pastPlans, outstandingPlans, sortedPlans, profileDailyPlans } = useDailyPlan(); const fullWidth = useRecoilValue(fullWidthState); - const [currentTab, setCurrentTab] = useState('Today Tasks'); + const [currentTab, setCurrentTab] = useState(defaultTab || 'Today Tasks'); const tabsScreens = { 'Today Tasks': , @@ -33,6 +38,10 @@ export function UserProfilePlans() { Outstanding: }; + useEffect(() => { + window.localStorage.setItem('daily-plan-tab', currentTab); + }, [currentTab]); + return (
diff --git a/apps/web/locales/ar.json b/apps/web/locales/ar.json index 70fac3c32..71495801e 100644 --- a/apps/web/locales/ar.json +++ b/apps/web/locales/ar.json @@ -224,7 +224,13 @@ "SENT_EMAIL_VERIFICATION_RESEND": "لإعادة إرسال الرمز إذا لم تتلقاه.", "INVITATIONS": "لقد تمت دعوتك للانضمام إلى", "CONFIRM_ACCEPT_INVITATION": "هل أنت متأكد من رغبتك في قبول الدعوة؟", - "CONFIRM_REJECT_INVITATION": "هل أنت متأكد من رغبتك في رفض الدعوة؟" + "CONFIRM_REJECT_INVITATION": "هل أنت متأكد من رغبتك في رفض الدعوة؟", + "OUTSTANDING_NOTIFICATIONS": { + "SUBJECT": "لديك", + "USER_LABEL": "مهام غير مكتملة، يرجى التحقق منها", + "OUTSTANDING_VIEW": "عرض المهام المعلقة", + "VIEW_BUTTON": "عرض" + } }, "kanban": { "KANBAN_BOARD": "لوحة كانبان" diff --git a/apps/web/locales/bg.json b/apps/web/locales/bg.json index d9bd9d69a..05e3a4394 100644 --- a/apps/web/locales/bg.json +++ b/apps/web/locales/bg.json @@ -224,7 +224,13 @@ "SENT_EMAIL_VERIFICATION_RESEND": "за да изпратите кода отново, ако не сте го получили.", "INVITATIONS": "Поканени сте да се присъедините към", "CONFIRM_ACCEPT_INVITATION": "Сигурни ли сте, че искате да приемете поканата?", - "CONFIRM_REJECT_INVITATION": "Сигурни ли сте, че искате да отхвърлите поканата?" + "CONFIRM_REJECT_INVITATION": "Сигурни ли сте, че искате да отхвърлите поканата?", + "OUTSTANDING_NOTIFICATIONS": { + "SUBJECT": "Имате", + "USER_LABEL": "незавършени задачи, моля, проверете", + "OUTSTANDING_VIEW": "Преглед на изчакващите задачи", + "VIEW_BUTTON": "Преглед" + } }, "kanban": { "KANBAN_BOARD": "Канбан дъска" diff --git a/apps/web/locales/de.json b/apps/web/locales/de.json index 9f9d6c088..6c412848d 100644 --- a/apps/web/locales/de.json +++ b/apps/web/locales/de.json @@ -224,7 +224,13 @@ "SENT_EMAIL_VERIFICATION_RESEND": " um den Code erneut zu senden, falls Sie ihn nicht erhalten haben.", "INVITATIONS": "Sie wurden eingeladen, beizutreten", "CONFIRM_ACCEPT_INVITATION": "Möchten Sie die Einladung wirklich annehmen?", - "CONFIRM_REJECT_INVITATION": "Möchten Sie die Einladung wirklich ablehnen?" + "CONFIRM_REJECT_INVITATION": "Möchten Sie die Einladung wirklich ablehnen?", + "OUTSTANDING_NOTIFICATIONS": { + "SUBJECT": "Sie haben", + "USER_LABEL": "unvollständige Aufgaben, bitte überprüfen", + "OUTSTANDING_VIEW": "Ausstehende Ansicht", + "VIEW_BUTTON": "Ansehen" + } }, "kanban": { "KANBAN_BOARD": "Kanban-Board" diff --git a/apps/web/locales/en.json b/apps/web/locales/en.json index 55c9c5bd1..23c2cb9c4 100644 --- a/apps/web/locales/en.json +++ b/apps/web/locales/en.json @@ -224,7 +224,13 @@ "SENT_EMAIL_VERIFICATION_RESEND": " to resend the code, if you did not received it.", "INVITATIONS": "You've been invited to join", "CONFIRM_ACCEPT_INVITATION": "Are you sure you want to accept the invitation?", - "CONFIRM_REJECT_INVITATION": "Are you sure you want to reject the invitation?" + "CONFIRM_REJECT_INVITATION": "Are you sure you want to reject the invitation?", + "OUTSTANDING_NOTIFICATIONS": { + "SUBJECT": "You've", + "USER_LABEL": "uncompleted tasks, please check in", + "OUTSTANDING_VIEW": "Outstanding View", + "VIEW_BUTTON": "View" + } }, "profile": { "BREADCRUMB": "[\"Member Tasks\"]", diff --git a/apps/web/locales/es.json b/apps/web/locales/es.json index 93ac932f9..48245f713 100644 --- a/apps/web/locales/es.json +++ b/apps/web/locales/es.json @@ -224,7 +224,13 @@ "SENT_EMAIL_VERIFICATION_RESEND": " para reenviar el código, si no lo recibiste.", "INVITATIONS": "Has sido invitado a unirte a", "CONFIRM_ACCEPT_INVITATION": "¿Estás seguro de que deseas aceptar la invitación?", - "CONFIRM_REJECT_INVITATION": "¿Estás seguro de que deseas rechazar la invitación?" + "CONFIRM_REJECT_INVITATION": "¿Estás seguro de que deseas rechazar la invitación?", + "OUTSTANDING_NOTIFICATIONS": { + "SUBJECT": "Tienes", + "USER_LABEL": "tareas incompletas, por favor revisa", + "OUTSTANDING_VIEW": "Vista pendiente", + "VIEW_BUTTON": "Ver" + } }, "kanban": { "KANBAN_BOARD": "tablero kanban" diff --git a/apps/web/locales/fr.json b/apps/web/locales/fr.json index 4c36ec885..58ac24882 100644 --- a/apps/web/locales/fr.json +++ b/apps/web/locales/fr.json @@ -224,7 +224,13 @@ "SENT_EMAIL_VERIFICATION_RESEND": " pour renvoyer le code si vous ne l'avez pas reçu.", "INVITATIONS": "Vous avez été invité à rejoindre", "CONFIRM_ACCEPT_INVITATION": "Êtes-vous sûr de vouloir accepter l'invitation ?", - "CONFIRM_REJECT_INVITATION": "Êtes-vous sûr de vouloir refuser l'invitation ?" + "CONFIRM_REJECT_INVITATION": "Êtes-vous sûr de vouloir refuser l'invitation ?", + "OUTSTANDING_NOTIFICATIONS": { + "SUBJECT": "Vous avez", + "USER_LABEL": "des tâches incomplètes, veuillez vérifier", + "OUTSTANDING_VIEW": "Vue des tâches en attente", + "VIEW_BUTTON": "Voir" + } }, "kanban": { "KANBAN_BOARD": "Tableau Kanban" diff --git a/apps/web/locales/he.json b/apps/web/locales/he.json index 4760f8573..de09d3472 100644 --- a/apps/web/locales/he.json +++ b/apps/web/locales/he.json @@ -224,7 +224,13 @@ "SENT_EMAIL_VERIFICATION_RESEND": " כדי לשלוח מחדש את הקוד, אם לא קיבלת אותו.", "INVITATIONS": "הוזמנת להצטרף ל-", "CONFIRM_ACCEPT_INVITATION": "האם אתה בטוח שברצונך לקבל את ההזמנה?", - "CONFIRM_REJECT_INVITATION": "האם אתה בטוח שברצונך לדחות את ההזמנה?" + "CONFIRM_REJECT_INVITATION": "האם אתה בטוח שברצונך לדחות את ההזמנה?", + "OUTSTANDING_NOTIFICATIONS": { + "SUBJECT": "יש לך", + "USER_LABEL": "משימות שלא הושלמו, נא לבדוק", + "OUTSTANDING_VIEW": "תצוגה ממתינה", + "VIEW_BUTTON": "הצג" + } }, "kanban": { "KANBAN_BOARD": "לוח Kanban" diff --git a/apps/web/locales/it.json b/apps/web/locales/it.json index 437a409f5..59a30ce9f 100644 --- a/apps/web/locales/it.json +++ b/apps/web/locales/it.json @@ -224,7 +224,13 @@ "SENT_EMAIL_VERIFICATION_RESEND": " per inviare nuovamente il codice, se non l'hai ricevuto.", "INVITATIONS": "Sei stato invitato a unirti a", "CONFIRM_ACCEPT_INVITATION": "Sei sicuro di voler accettare l'invito?", - "CONFIRM_REJECT_INVITATION": "Sei sicuro di voler rifiutare l'invito?" + "CONFIRM_REJECT_INVITATION": "Sei sicuro di voler rifiutare l'invito?", + "OUTSTANDING_NOTIFICATIONS": { + "SUBJECT": "Hai", + "USER_LABEL": "compiti incompleti, controlla per favore", + "OUTSTANDING_VIEW": "Vista delle attività in sospeso", + "VIEW_BUTTON": "Vedi" + } }, "kanban": { "KANBAN_BOARD": "Tabellone Kanban" diff --git a/apps/web/locales/nl.json b/apps/web/locales/nl.json index dd88cca6f..3748ca039 100644 --- a/apps/web/locales/nl.json +++ b/apps/web/locales/nl.json @@ -224,7 +224,13 @@ "SENT_EMAIL_VERIFICATION_RESEND": "om de code opnieuw te verzenden, voor het geval u deze niet heeft ontvangen.", "INVITATIONS": "U bent uitgenodigd om lid te worden van", "CONFIRM_ACCEPT_INVITATION": "Weet u zeker dat u de uitnodiging wilt accepteren?", - "CONFIRM_REJECT_INVITATION": "Weet u zeker dat u de uitnodiging wilt afwijzen?" + "CONFIRM_REJECT_INVITATION": "Weet u zeker dat u de uitnodiging wilt afwijzen?", + "OUTSTANDING_NOTIFICATIONS": { + "SUBJECT": "Je hebt", + "USER_LABEL": "onvoltooide taken, controleer alsjeblieft", + "OUTSTANDING_VIEW": "Uitzicht op uitstaande taken", + "VIEW_BUTTON": "Bekijk" + } }, "kanban": { "KANBAN_BOARD": "Kanban-bord" diff --git a/apps/web/locales/pl.json b/apps/web/locales/pl.json index 7a4ac5348..e6baa5879 100644 --- a/apps/web/locales/pl.json +++ b/apps/web/locales/pl.json @@ -224,7 +224,13 @@ "SENT_EMAIL_VERIFICATION_RESEND": " aby ponownie wysłać kod, jeśli go nie otrzymałeś.", "INVITATIONS": "Zostałeś zaproszony do dołączenia do", "CONFIRM_ACCEPT_INVITATION": "Czy na pewno chcesz zaakceptować zaproszenie?", - "CONFIRM_REJECT_INVITATION": "Czy na pewno chcesz odrzucić zaproszenie?" + "CONFIRM_REJECT_INVITATION": "Czy na pewno chcesz odrzucić zaproszenie?", + "OUTSTANDING_NOTIFICATIONS": { + "SUBJECT": "Masz", + "USER_LABEL": "niezakończone zadania, proszę sprawdź", + "OUTSTANDING_VIEW": "Widok zadań do wykonania", + "VIEW_BUTTON": "Zobacz" + } }, "kanban": { "KANBAN_BOARD": "Tablica Kanbana" diff --git a/apps/web/locales/pt.json b/apps/web/locales/pt.json index eeb4c8125..da21ac9f7 100644 --- a/apps/web/locales/pt.json +++ b/apps/web/locales/pt.json @@ -224,7 +224,13 @@ "SENT_EMAIL_VERIFICATION_RESEND": " para reenviar o código, caso não o tenha recebido.", "INVITATIONS": "Você foi convidado a participar de", "CONFIRM_ACCEPT_INVITATION": "Você tem certeza de que deseja aceitar o convite?", - "CONFIRM_REJECT_INVITATION": "Você tem certeza de que deseja rejeitar o convite?" + "CONFIRM_REJECT_INVITATION": "Você tem certeza de que deseja rejeitar o convite?", + "OUTSTANDING_NOTIFICATIONS": { + "SUBJECT": "Você tem", + "USER_LABEL": "tarefas não concluídas, por favor verifique", + "OUTSTANDING_VIEW": "Visualização de pendências", + "VIEW_BUTTON": "Ver" + } }, "kanban": { "KANBAN_BOARD": "Quadro Kanban" diff --git a/apps/web/locales/ru.json b/apps/web/locales/ru.json index 580ff8755..73ab9203b 100644 --- a/apps/web/locales/ru.json +++ b/apps/web/locales/ru.json @@ -224,7 +224,13 @@ "SENT_EMAIL_VERIFICATION_RESEND": " для повторной отправки кода, если вы его не получили.", "INVITATIONS": "Вас пригласили присоединиться к", "CONFIRM_ACCEPT_INVITATION": "Вы уверены, что хотите принять приглашение?", - "CONFIRM_REJECT_INVITATION": "Вы уверены, что хотите отклонить приглашение?" + "CONFIRM_REJECT_INVITATION": "Вы уверены, что хотите отклонить приглашение?", + "OUTSTANDING_NOTIFICATIONS": { + "SUBJECT": "У вас есть", + "USER_LABEL": "незавершенные задачи, пожалуйста, проверьте", + "OUTSTANDING_VIEW": "Просмотр незавершенных задач", + "VIEW_BUTTON": "Просмотр" + } }, "kanban": { "KANBAN_BOARD": "Канбан-доска" diff --git a/apps/web/locales/zh.json b/apps/web/locales/zh.json index 6062a5093..5102546f0 100644 --- a/apps/web/locales/zh.json +++ b/apps/web/locales/zh.json @@ -224,7 +224,13 @@ "SENT_EMAIL_VERIFICATION_RESEND": " 重新发送验证码。", "INVITATIONS": "您被邀请加入", "CONFIRM_ACCEPT_INVITATION": "您确定要接受邀请吗?", - "CONFIRM_REJECT_INVITATION": "您确定要拒绝邀请吗?" + "CONFIRM_REJECT_INVITATION": "您确定要拒绝邀请吗?", + "OUTSTANDING_NOTIFICATIONS": { + "SUBJECT": "您有", + "USER_LABEL": "未完成的任务,请检查", + "OUTSTANDING_VIEW": "待处理视图", + "VIEW_BUTTON": "查看" + } }, "kanban": { "KANBAN_BOARD": "看板"