diff --git a/apps/web/.eslintrc.js b/apps/web/.eslintrc.js index b88cc6af..7bdf09b7 100644 --- a/apps/web/.eslintrc.js +++ b/apps/web/.eslintrc.js @@ -93,7 +93,7 @@ module.exports = { position: 'after', }, { - pattern: '@hooks/**', + pattern: 'hooks/**', group: 'internal', position: 'after', }, diff --git a/apps/web/src/components/Auth/SignupForm.tsx b/apps/web/src/components/Auth/SignupForm.tsx index 321bea56..9e6d3cde 100644 --- a/apps/web/src/components/Auth/SignupForm.tsx +++ b/apps/web/src/components/Auth/SignupForm.tsx @@ -4,6 +4,7 @@ import { useForm } from 'react-hook-form'; import toast from 'react-hot-toast'; import { Flex, Box, Button, Text, Field, InputText } from '@wraft/ui'; import { BrandLogoIcon } from '@wraft/icon'; +import styled from '@emotion/styled'; import Link from 'common/NavLink'; import ErrorMessages from 'common/ErrorMessages'; @@ -12,6 +13,89 @@ import { postAPI } from 'utils/models'; import WaitlistPrompt from './WaitlistPrompt'; +const NameFields = styled(Flex)` + display: flex; + flex-direction: row; + gap: 16px; + margin-bottom: 24px; + + @media (max-width: 768px) { + flex-direction: column; + } +`; + +const ResponsiveLogo = styled(BrandLogoIcon)` + width: 7rem; + height: 3rem; + + @media (max-width: 768px) { + width: 5rem; + height: 2rem; + } + + @media (max-width: 480px) { + width: 5rem; + height: 1.8rem; + } +`; + +const LogoWrapper = styled(Box)` + position: absolute; + top: 80px; + left: 80px; + + @media (max-width: 1024px) { + top: 60px; + left: 40px; + } + + @media (max-width: 768px) { + position: relative; + top: 0; + left: 0; + display: flex; + justify-content: flex-start; + width: 100%; + padding-left: 16px; + margin-bottom: 24px; + } + + @media (max-width: 480px) { + padding-left: 12px; + margin-bottom: 20px; + } +`; + +const FormCard = styled(Flex)` + width: 500px; + flex-direction: column; + background: white; + border-radius: 12px; + padding: 32px; + + @media (max-width: 768px) { + width: 100%; + max-width: 90%; + } + + @media (max-width: 480px) { + max-width: 95%; + padding: 20px; + } +`; + +const PageWrapper = styled(Flex)` + justify-content: center; + align-items: flex-start; + padding: 6rem; + + @media (max-width: 768px) { + flex-direction: column; + align-items: center; + padding: 2rem 1rem; + } +`; + const SignupForm = () => { const { register, @@ -21,6 +105,7 @@ const SignupForm = () => { mode: 'onSubmit', resolver: zodResolver(SignUpSchema), }); + const [showSuccess, setShowSuccess] = useState(false); const homePageUrl = process.env.homePageUrl || '/'; @@ -48,24 +133,22 @@ const SignupForm = () => { {showSuccess ? ( ) : ( - - + + - + - - + + + Join Wraft + - + { /> - - + + + { + Already joined? @@ -113,8 +198,8 @@ const SignupForm = () => { Privacy Policy. - - + + )} ); diff --git a/apps/web/src/components/DocumentView/DocumentSidebar/ContentInfoBlock.tsx b/apps/web/src/components/DocumentView/DocumentSidebar/ContentInfoBlock.tsx index 50464a32..be725db2 100644 --- a/apps/web/src/components/DocumentView/DocumentSidebar/ContentInfoBlock.tsx +++ b/apps/web/src/components/DocumentView/DocumentSidebar/ContentInfoBlock.tsx @@ -142,7 +142,10 @@ export const EditMenus = ({ id, nextState }: EditMenuProps) => { /> - + setMailPopupOpen(false)}> <> {isMailPopupOpen && ( diff --git a/apps/web/src/components/ImportTemplate/ImporterWrapper.tsx b/apps/web/src/components/ImportTemplate/ImporterWrapper.tsx index 4ca244d0..23b09022 100644 --- a/apps/web/src/components/ImportTemplate/ImporterWrapper.tsx +++ b/apps/web/src/components/ImportTemplate/ImporterWrapper.tsx @@ -77,7 +77,6 @@ function ImporterApp() { useState(defaultActionState); const [imported, setImported] = useState(); - const [errors, setErrors] = useState([]); const handleNext = () => { @@ -88,11 +87,10 @@ function ImporterApp() { /* * Initiate Import process - * @param id string - * import templates from uploaded template aset + * import templates from uploaded template asset */ - const importNow = (id: string, _onDone?: any) => { + const importNow = () => { if (!formData) { handleNext(); setActionState({ state: ActionState.COMPLETED }); @@ -105,13 +103,18 @@ function ImporterApp() { }); postAPI(`global_asset/import`, formData) .then((res: ImportedItems) => { - toast.success(`Successfully imported template: ${id}`); + const templateName = + res.items?.find((item) => item.item_type === 'data_template') + ?.title || + res.items?.[0]?.title || + res.items?.[0]?.name; + + toast.success(`Successfully imported: ${templateName}`); setImported(res); handleNext(); setActionState({ state: ActionState.COMPLETED, }); - _onDone && _onDone(res); }) .catch((error: any) => { setErrors(error); @@ -216,7 +219,6 @@ function ImporterApp() { /> )} - {currentStep === 2 && ( = ({ const handleViewAll = () => { router.push('/notifications'); }; - const handleSettingsClick = () => { - router.push('/manage/workspace/notification-settings'); + router.push({ + pathname: '/manage/workspace/notification-settings', + query: { from: 'notifications' }, + }); }; return ( diff --git a/apps/web/src/components/Notification/NotificationList.tsx b/apps/web/src/components/Notification/NotificationList.tsx index 03c7326c..6917fd1b 100644 --- a/apps/web/src/components/Notification/NotificationList.tsx +++ b/apps/web/src/components/Notification/NotificationList.tsx @@ -3,7 +3,7 @@ import { useRouter } from 'next/router'; import { Avatar, Pagination, Box, Text, Flex } from '@wraft/ui'; import styled from '@xstyled/emotion'; -import useNotifications from '@hooks/useNotifications'; +import useNotifications from 'hooks/useNotifications'; import { TimeAgo } from 'common/Atoms'; import { diff --git a/apps/web/src/components/Sidebar/Header.tsx b/apps/web/src/components/Sidebar/Header.tsx index a3c42316..4abaa18e 100644 --- a/apps/web/src/components/Sidebar/Header.tsx +++ b/apps/web/src/components/Sidebar/Header.tsx @@ -3,9 +3,10 @@ import { useRouter } from 'next/router'; import NavLink from 'next/link'; import { Button, DropdownMenu, Box, Flex, Text, Modal } from '@wraft/ui'; import toast from 'react-hot-toast'; -import { CaretDown, Gear, Plus } from '@phosphor-icons/react'; +import { CaretDownIcon, GearIcon, PlusIcon } from '@phosphor-icons/react'; import NotificationDropdown from 'components/Notification/NotificationDropdown'; +import { useNotificationSidebarMode } from 'hooks/useNotificationSidebarMode'; import DefaultAvatar from 'common/DefaultAvatar'; import { IconFrame } from 'common/Atoms'; import { useAuth } from 'contexts/AuthContext'; @@ -20,6 +21,7 @@ const Header = ({ }) => { const [isOpen, setIsOpen] = useState(false); const [createdId, setCreatedId] = useState(); + const { clearNotificationSidebarMode } = useNotificationSidebarMode(); const router = useRouter(); const { organisations, userProfile, login } = useAuth(); @@ -69,7 +71,7 @@ const Header = ({ lines={1}> {userProfile?.currentOrganisation?.name} - + )} @@ -102,7 +104,12 @@ const Header = ({ - + )} @@ -136,7 +143,7 @@ const Header = ({ @@ -152,7 +159,7 @@ const Header = ({ size="xs" onClick={toggleCreateDocument}> - + diff --git a/apps/web/src/components/Template/TemplateForm.tsx b/apps/web/src/components/Template/TemplateForm.tsx index 8aba0021..ab46a2df 100644 --- a/apps/web/src/components/Template/TemplateForm.tsx +++ b/apps/web/src/components/Template/TemplateForm.tsx @@ -388,7 +388,7 @@ const TemplateEditor = () => { /> - {selectedVariant && selectedVariant.fields && !templateId && ( + {selectedVariant && selectedVariant.fields && ( { hint="The variant type cannot be modified once saved. Please select the correct one." required error={errors?.variant?.message}> - item && item.name} - name={name} - placeholder="Search Variant" - minChars={0} - value={value} - onChange={(variant: any) => { - if (variant?.id) { - onChange(variant); - fetchContentTypeDetails(variant?.id); - } - }} - renderItem={(item: any) => ( - - {item.name} - - )} - search={onSearchVariants} - /> + {templateId ? ( + + ) : ( + item && item.name} + name={name} + placeholder="Search Variant" + minChars={0} + value={value} + onChange={(variant: any) => { + if (variant?.id) { + onChange(variant); + fetchContentTypeDetails(variant?.id); + } + }} + renderItem={(item: any) => ( + + {item.name} + + )} + search={onSearchVariants} + disabled={!!templateId} + /> + )} )} /> diff --git a/apps/web/src/components/Template/TemplateList.tsx b/apps/web/src/components/Template/TemplateList.tsx index d7d5835d..0297387f 100644 --- a/apps/web/src/components/Template/TemplateList.tsx +++ b/apps/web/src/components/Template/TemplateList.tsx @@ -75,9 +75,6 @@ const columns = ({ {hasPermission('template', 'manage') && ( - - Edit - onCloneTemplete(row.original)}> Clone diff --git a/apps/web/src/components/User/ProfileForm.tsx b/apps/web/src/components/User/ProfileForm.tsx index 2d789c44..82725d25 100644 --- a/apps/web/src/components/User/ProfileForm.tsx +++ b/apps/web/src/components/User/ProfileForm.tsx @@ -77,6 +77,15 @@ const EditText = styled(Box)` transition: opacity 0.2s; `; +const StyledDateInput = styled(InputText)` + @media (prefers-color-scheme: dark) { + &[type="date"] { + &::-webkit-calendar-picker-indicator { + filter: invert(0.4); + } + +`; + const Form = () => { const { accessToken } = useAuth(); const [profile, setProfile] = useState(); @@ -219,7 +228,7 @@ const Form = () => { - + diff --git a/apps/web/src/components/Variants/VariantForm.tsx b/apps/web/src/components/Variants/VariantForm.tsx index 0d97f0d4..ea6ec17a 100644 --- a/apps/web/src/components/Variants/VariantForm.tsx +++ b/apps/web/src/components/Variants/VariantForm.tsx @@ -354,9 +354,9 @@ const VariantForm = ({ step = 0, setIsOpen, setRerender }: Props) => { return false; }; - const onSearchLayouts = async () => { + const onSearchLayouts = async (query: string) => { try { - const response: any = await fetchAPI('layouts'); + const response: any = await fetchAPI(`layouts?name=${query}`); if (!response || !response.layouts) { throw new Error('Invalid response structure'); @@ -369,9 +369,9 @@ const VariantForm = ({ step = 0, setIsOpen, setRerender }: Props) => { } }; - const onSearchFlows = async () => { + const onSearchFlows = async (query: string) => { try { - const response: any = await fetchAPI('flows'); + const response: any = await fetchAPI(`flows?name=${query}`); if (!response || !response.flows) { throw new Error('Invalid response structure'); @@ -389,9 +389,9 @@ const VariantForm = ({ step = 0, setIsOpen, setRerender }: Props) => { } }; - const onSearchThemes = async () => { + const onSearchThemes = async (query: string) => { try { - const response: any = await fetchAPI('themes'); + const response: any = await fetchAPI(`themes?name=${query}`); if (!response || !response.themes) { throw new Error('Invalid response structure'); diff --git a/apps/web/src/constants/menuLinks.tsx b/apps/web/src/constants/menuLinks.tsx index 594d12b6..1f7c4710 100644 --- a/apps/web/src/constants/menuLinks.tsx +++ b/apps/web/src/constants/menuLinks.tsx @@ -55,11 +55,6 @@ export const PersonalWorkspaceLinks: menuLinksProps[] = [ path: '/manage/workspace', permissions: ['workspace.update', 'workspace.delete', 'workspace.invite'], }, - { - name: 'Fields', - path: '/manage/fields', - permissions: ['workspace.update', 'workspace.delete', 'workspace.invite'], - }, ]; export const workspaceLinks: menuLinksProps[] = [ @@ -171,3 +166,9 @@ export const workspaceMenu: menuLinksProps[] = [ ] : []), ]; + +export const NOTIFICATION_LINK_NAMES = ['General', 'Notification']; + +export const notificationSidebarLinks: menuLinksProps[] = workspaceLinks.filter( + (link) => NOTIFICATION_LINK_NAMES.includes(link.name), +); diff --git a/apps/web/src/hooks/useNotificationSidebarMode.ts b/apps/web/src/hooks/useNotificationSidebarMode.ts new file mode 100644 index 00000000..d2b54923 --- /dev/null +++ b/apps/web/src/hooks/useNotificationSidebarMode.ts @@ -0,0 +1,55 @@ +import { useEffect, useState } from 'react'; +import { useRouter } from 'next/router'; + +import { + workspaceLinks, + PersonalWorkspaceLinks, + notificationSidebarLinks, +} from '@constants/menuLinks'; + +const NOTIFICATION_MODE_KEY = 'wraft_from_notifications'; + +export function useNotificationSidebarMode(currentOrgName?: string) { + const router = useRouter(); + const [fromNotification, setFromNotification] = useState(false); + + const enableNotificationMode = () => { + sessionStorage.setItem(NOTIFICATION_MODE_KEY, '1'); + setFromNotification(true); + }; + + const disableNotificationMode = () => { + sessionStorage.removeItem(NOTIFICATION_MODE_KEY); + setFromNotification(false); + }; + + useEffect(() => { + if (!router.isReady) return; + + const queryFrom = Array.isArray(router.query.from) + ? router.query.from[0] + : router.query.from; + + if (queryFrom === 'notifications') { + enableNotificationMode(); + router.replace(router.pathname, undefined, { shallow: true }); + return; + } + + const isFlagSet = sessionStorage.getItem(NOTIFICATION_MODE_KEY) === '1'; + setFromNotification(isFlagSet); + }, [router.isReady, router.query.from]); + + const itemsForSidebar = + currentOrgName !== 'Personal' + ? workspaceLinks + : fromNotification + ? notificationSidebarLinks + : PersonalWorkspaceLinks; + + return { + fromNotification, + itemsForSidebar, + clearNotificationSidebarMode: disableNotificationMode, + }; +} diff --git a/apps/web/src/pages/manage/workspace/fields.tsx b/apps/web/src/pages/manage/workspace/fields.tsx index 0b9f6e02..22b43c8f 100644 --- a/apps/web/src/pages/manage/workspace/fields.tsx +++ b/apps/web/src/pages/manage/workspace/fields.tsx @@ -1,5 +1,6 @@ -import { FC } from 'react'; +import { FC, useEffect } from 'react'; import Head from 'next/head'; +import router from 'next/router'; import { Flex } from '@wraft/ui'; import { workspaceLinks } from '@constants/menuLinks'; @@ -8,41 +9,55 @@ import ManageSidebar from 'common/ManageSidebar'; import Page from 'common/PageFrame'; import PageHeader from 'common/PageHeader'; import { PageInner } from 'common/Atoms'; +import { useAuth } from 'contexts/AuthContext'; const CompanyForm: FC = () => { + const { userProfile } = useAuth(); + const currentOrg = userProfile?.currentOrganisation?.name; + + useEffect(() => { + if (currentOrg === 'Personal') { + router.replace('/404'); + } + }, [currentOrg]); + + if (currentOrg === 'Personal') return null; + return ( - <> - - Manage Organization \ Fields | Wraft - - - - + (currentOrg !== 'Personal' || '') && ( + <> + + Manage Organization \ Fields | Wraft + + + + - - - - - + + + + + + - - - - + + + + ) ); }; diff --git a/apps/web/src/pages/manage/workspace/index.tsx b/apps/web/src/pages/manage/workspace/index.tsx index 89d5b3df..3deaa9a5 100644 --- a/apps/web/src/pages/manage/workspace/index.tsx +++ b/apps/web/src/pages/manage/workspace/index.tsx @@ -6,7 +6,7 @@ import toast from 'react-hot-toast'; import { Input, Label } from 'theme-ui'; import { Box, Field, Flex, InputText, Text, Button, Modal } from '@wraft/ui'; -import { PersonalWorkspaceLinks, workspaceLinks } from '@constants/menuLinks'; +import { useNotificationSidebarMode } from 'hooks/useNotificationSidebarMode'; import ManageSidebar from 'common/ManageSidebar'; import DefaultAvatar from 'common/DefaultAvatar'; import Page from 'common/PageFrame'; @@ -58,6 +58,7 @@ const Index: FC = () => { const orgId = userProfile?.organisation_id || null; const currentOrg = userProfile?.currentOrganisation || null; + const { itemsForSidebar } = useNotificationSidebarMode(currentOrg?.name); const [previewSource, setPreviewSource] = useState( undefined, @@ -235,13 +236,7 @@ const Index: FC = () => { - + { setRefresh((prev) => prev + 1); }; + useEffect(() => { + if (currentOrg === 'Personal') { + router.replace('/404'); + } + }, [currentOrg]); + + if (currentOrg === 'Personal') return null; + return ( (currentOrg !== 'Personal' || '') && ( <> diff --git a/apps/web/src/pages/manage/workspace/notification-settings.tsx b/apps/web/src/pages/manage/workspace/notification-settings.tsx index 8a643112..fcfe9419 100644 --- a/apps/web/src/pages/manage/workspace/notification-settings.tsx +++ b/apps/web/src/pages/manage/workspace/notification-settings.tsx @@ -2,14 +2,21 @@ import { FC } from 'react'; import Head from 'next/head'; import { Flex } from '@wraft/ui'; -import { workspaceLinks } from '@constants/menuLinks'; import NotificationSettings from 'components/Notification/NotificationSettings'; +import { useNotificationSidebarMode } from 'hooks/useNotificationSidebarMode'; import Page from 'common/PageFrame'; import PageHeader from 'common/PageHeader'; import ManageSidebar from 'common/ManageSidebar'; import { PageInner } from 'common/Atoms'; +import { useAuth } from 'contexts/AuthContext'; const WorkspaceNotificationSettings: FC = () => { + const { userProfile } = useAuth(); + const currentOrganisation = userProfile?.currentOrganisation; + const { itemsForSidebar } = useNotificationSidebarMode( + currentOrganisation?.name, + ); + return ( <> @@ -29,7 +36,7 @@ const WorkspaceNotificationSettings: FC = () => { /> - + diff --git a/apps/web/src/pages/manage/workspace/permissions.tsx b/apps/web/src/pages/manage/workspace/permissions.tsx index 291c45cb..bdddf275 100644 --- a/apps/web/src/pages/manage/workspace/permissions.tsx +++ b/apps/web/src/pages/manage/workspace/permissions.tsx @@ -1,5 +1,6 @@ -import React, { FC } from 'react'; +import React, { FC, useEffect } from 'react'; import Head from 'next/head'; +import { useRouter } from 'next/router'; import { Flex } from '@wraft/ui'; import { workspaceLinks } from '@constants/menuLinks'; @@ -12,8 +13,19 @@ import { useAuth } from 'contexts/AuthContext'; const Index: FC = () => { const { userProfile } = useAuth(); + const router = useRouter(); + const currentOrg = userProfile?.currentOrganisation?.name; + + useEffect(() => { + if (currentOrg === 'Personal') { + router.replace('/404'); + } + }, [currentOrg, router]); + + if (currentOrg === 'Personal') return null; + return ( - (userProfile?.currentOrganisation?.name !== 'Personal' || '') && ( + userProfile?.currentOrganisation?.name !== 'Personal' && ( <> Permission | Wraft diff --git a/apps/web/src/pages/manage/workspace/roles.tsx b/apps/web/src/pages/manage/workspace/roles.tsx index 1dd4959e..f55f7106 100644 --- a/apps/web/src/pages/manage/workspace/roles.tsx +++ b/apps/web/src/pages/manage/workspace/roles.tsx @@ -1,5 +1,6 @@ -import React, { FC, useState } from 'react'; +import React, { FC, useEffect, useState } from 'react'; import Head from 'next/head'; +import { useRouter } from 'next/router'; import { Spinner } from '@wraft/ui'; import { MagnifyingGlassIcon, PlusIcon } from '@phosphor-icons/react'; import { Button, Flex, Box, InputText, Drawer, useDrawer } from '@wraft/ui'; @@ -22,6 +23,16 @@ const Index: FC = () => { const { userProfile } = useAuth(); const roleDrawer = useDrawer(); const { hasPermission } = usePermission(); + const router = useRouter(); + const currentOrg = userProfile?.currentOrganisation?.name; + + useEffect(() => { + if (currentOrg === 'Personal') { + router.replace('/404'); + } + }, [currentOrg, router]); + + if (currentOrg === 'Personal') return null; return ( (userProfile?.currentOrganisation?.name !== 'Personal' || '') && ( diff --git a/apps/web/src/pages/manage/workspace/webhooks/index.tsx b/apps/web/src/pages/manage/workspace/webhooks/index.tsx index 5423d407..71a2e169 100644 --- a/apps/web/src/pages/manage/workspace/webhooks/index.tsx +++ b/apps/web/src/pages/manage/workspace/webhooks/index.tsx @@ -1,5 +1,6 @@ -import React, { useState } from 'react'; +import React, { useEffect, useState } from 'react'; import Head from 'next/head'; +import { useRouter } from 'next/router'; import { Button, Drawer, useDrawer, Flex } from '@wraft/ui'; import { Plus } from '@phosphor-icons/react'; @@ -23,6 +24,8 @@ const WebhooksPage = () => { const canCreateWebhooks = hasPermission('webhook', 'manage'); const createDrawer = useDrawer(); const editDrawer = useDrawer(); + const router = useRouter(); + const currentOrg = userProfile?.currentOrganisation?.name; const handleCreateSuccess = () => { setIsCreateDrawerOpen(false); @@ -42,6 +45,14 @@ const WebhooksPage = () => { setRefreshKey((prevKey) => prevKey + 1); }; + useEffect(() => { + if (currentOrg === 'Personal') { + router.replace('/404'); + } + }, [currentOrg, router]); + + if (currentOrg === 'Personal') return null; + return ( (userProfile?.currentOrganisation?.name !== 'Personal' || '') && ( <> diff --git a/apps/web/src/schemas/template.ts b/apps/web/src/schemas/template.ts index 86ed68cd..a94a5ca8 100644 --- a/apps/web/src/schemas/template.ts +++ b/apps/web/src/schemas/template.ts @@ -10,6 +10,7 @@ export const TemplateSchema = z.object({ .regex(nameRegex, 'Allows only letters, numbers and spaces'), variant: z.object({ id: z.string().nonempty({ message: 'Variant is required' }), + name: z.string().optional(), }), title_template: z.string().optional(), serialized: z.string().optional(), diff --git a/apps/web/tsconfig.json b/apps/web/tsconfig.json index b7e33534..cb804a90 100644 --- a/apps/web/tsconfig.json +++ b/apps/web/tsconfig.json @@ -6,7 +6,7 @@ "paths": { "common/*": ["components/common/*"], "@constants/*": ["constants/*"], - "@hooks/*": ["hooks/*"] + "hooks/*": ["hooks/*"] }, "lib": ["dom", "es2017"], diff --git a/packages/editor/src/components/editor.tsx b/packages/editor/src/components/editor.tsx index 9fa4620e..86113b37 100644 --- a/packages/editor/src/components/editor.tsx +++ b/packages/editor/src/components/editor.tsx @@ -3,8 +3,8 @@ import { ProseKit } from "prosekit/react"; import { useMemo, useImperativeHandle, forwardRef } from "react"; import { ListDOMSerializer } from "prosekit/extensions/list"; import type { Node } from "@prosekit/pm/model"; -import { markdownFromHTML } from "@helpers/markdown"; import type { ProsemirrorNodeJSON } from "prosemirror-flat-list"; +import { markdownFromHTML } from "@helpers/markdown"; import { migrateDocJSON } from "@helpers/migrate"; import { defineDefaultExtension } from "./extension"; import InlineMenu from "./inline-menu"; @@ -87,10 +87,15 @@ export const Editor = forwardRef( return ( -
+ + {!isReadonly && ( +
+ +
+ )} + - {!isReadonly && } {!isReadonly && } @@ -101,7 +106,7 @@ export const Editor = forwardRef(
-
+ {" "}
); }, diff --git a/packages/editor/src/components/extension.ts b/packages/editor/src/components/extension.ts index 30dd6cdf..23cce94a 100644 --- a/packages/editor/src/components/extension.ts +++ b/packages/editor/src/components/extension.ts @@ -17,6 +17,7 @@ import { defineYjs } from "prosekit/extensions/yjs"; import type { Awareness } from "y-protocols/awareness"; import type * as Y from "yjs"; import { defineReadonly } from "prosekit/extensions/readonly"; +import { defineCommitViewer } from "prosekit/extensions/commit"; import type { HolderExtension } from "@extensions/holder"; import { defineHolder } from "@extensions/holder"; import { defineFancyParagraph } from "@extensions/paragraph"; @@ -29,7 +30,6 @@ import type { SignatureExtension } from "@extensions/signature"; import { defineSignature } from "@extensions/signature"; import type { PageBreakExtension } from "@extensions/page-break"; import { definePageBreak } from "@extensions/page-break"; -import { defineCommitViewer } from "prosekit/extensions/commit"; import ImageView from "./image-view"; import SignatureView from "./signature-view"; import type { SignersConfig } from "./live-editor"; diff --git a/packages/editor/src/components/live-editor.tsx b/packages/editor/src/components/live-editor.tsx index e64a5dd1..b0f96762 100644 --- a/packages/editor/src/components/live-editor.tsx +++ b/packages/editor/src/components/live-editor.tsx @@ -16,10 +16,10 @@ import { prosemirrorJSONToYXmlFragment } from "y-prosemirror"; import { ListDOMSerializer } from "prosekit/extensions/list"; import type { Node } from "@prosekit/pm/model"; import type { Awareness } from "y-protocols/awareness"; +import type { ProsemirrorNodeJSON } from "prosemirror-flat-list"; import { markdownFromHTML } from "@helpers/markdown"; // import { IndexeddbPersistence } from "y-indexeddb"; import { migrateDocJSON } from "@helpers/migrate"; -import type { ProsemirrorNodeJSON } from "prosemirror-flat-list"; import { getUserColor } from "../lib/utils"; import { PhoenixChannelProvider } from "../lib/y-phoenix-channel"; import { defineCollaborativeExtension } from "./extension"; diff --git a/packages/editor/src/helpers/migrate.ts b/packages/editor/src/helpers/migrate.ts index aab6230f..6143ca7c 100644 --- a/packages/editor/src/helpers/migrate.ts +++ b/packages/editor/src/helpers/migrate.ts @@ -1,5 +1,5 @@ -import type { ListAttrs } from "@extensions/list-item"; import type { ProsemirrorNodeJSON } from "prosemirror-flat-list"; +import type { ListAttrs } from "@extensions/list-item"; function migrateNodes( nodes: ProsemirrorNodeJSON[], diff --git a/packages/ui/src/components/Paginate/index.tsx b/packages/ui/src/components/Paginate/index.tsx index a381ef68..d4a75e9d 100644 --- a/packages/ui/src/components/Paginate/index.tsx +++ b/packages/ui/src/components/Paginate/index.tsx @@ -16,6 +16,7 @@ export interface IProp { numberMarginPagesDisplayed?: number; totalEntries?: number; + entriesPerPage?: number; previousLabel?: string; nextLabel?: string; @@ -36,6 +37,7 @@ const Pagination: React.FC = ({ numberPageDisplayed = 2, numberMarginPagesDisplayed = 2, totalEntries, + entriesPerPage = 10, previousLabel = "Previous", nextLabel = "Next", breakLabel = "...", @@ -181,6 +183,15 @@ const Pagination: React.FC = ({ onHandlePageSelected(page, null); }; + const getShowingText = () => { + if (!totalEntries) return ""; + + const start = (currentPage - 1) * entriesPerPage + 1; + const end = Math.min(currentPage * entriesPerPage, totalEntries); + + return `Showing ${start} to ${end} of ${totalEntries}`; + }; + return ( <> @@ -230,7 +241,7 @@ const Pagination: React.FC = ({ {totalEntries && ( - {`Showing ${currentPage === 1 ? "" : currentPage - 1}0 of ${totalEntries}`} + {getShowingText()} )}