From 990b4dafd19582c0502788fbd2844f9365c6bea8 Mon Sep 17 00:00:00 2001 From: oneanotheruser Date: Thu, 30 Jan 2025 14:47:49 +0100 Subject: [PATCH] Work on document design page --- packages/sdk-react/jest.config.js | 1 + .../documentDesign/DocumentDesign.test.tsx | 61 +++++++++++- .../documentDesign/DocumentDesign.tsx | 8 +- .../DocumentDesignSelection.tsx | 8 +- .../DocumentDesignTemplate.tsx | 33 +++++-- .../DocumentDesignTemplatePreview.tsx | 36 +++++-- .../DocumentDesignTemplates.tsx | 3 + .../src/components/documentDesign/preview.png | Bin 0 -> 29133 bytes .../src/components/documentDesign/preview.svg | 19 ---- .../useDocumentTemplatePreviewLoader.ts | 93 ++++++++++++++++++ .../documentDesign/useDocumentTemplatesApi.ts | 8 +- .../src/core/context/MoniteContext.tsx | 12 ++- .../src/core/i18n/locales/en/messages.po | 42 ++++++++ .../documentTemplatesFixtures.ts | 13 --- packages/sdk-react/src/mocks/fileMock.ts | 1 + .../imageWithSkeleton/imageWithSkeleton.tsx | 9 +- packages/sdk-react/typings.d.ts | 5 + 17 files changed, 289 insertions(+), 63 deletions(-) create mode 100644 packages/sdk-react/src/components/documentDesign/preview.png delete mode 100644 packages/sdk-react/src/components/documentDesign/preview.svg create mode 100644 packages/sdk-react/src/components/documentDesign/useDocumentTemplatePreviewLoader.ts create mode 100644 packages/sdk-react/src/mocks/fileMock.ts diff --git a/packages/sdk-react/jest.config.js b/packages/sdk-react/jest.config.js index 998dcb5e4..98ce0d9d8 100644 --- a/packages/sdk-react/jest.config.js +++ b/packages/sdk-react/jest.config.js @@ -47,6 +47,7 @@ module.exports = { }, moduleNameMapper: { '^@/(.*)$': '/src/$1', + '\\.png$': '/src/mocks/fileMock.ts', }, collectCoverageFrom: [ 'src/**/*.{js,jsx,ts,tsx}', diff --git a/packages/sdk-react/src/components/documentDesign/DocumentDesign.test.tsx b/packages/sdk-react/src/components/documentDesign/DocumentDesign.test.tsx index 0c6abe773..85a14ff60 100644 --- a/packages/sdk-react/src/components/documentDesign/DocumentDesign.test.tsx +++ b/packages/sdk-react/src/components/documentDesign/DocumentDesign.test.tsx @@ -1,10 +1,13 @@ import { documentTemplateList } from '@/mocks/documentTemplates'; import { renderWithClient } from '@/utils/test-utils'; -import { fireEvent, screen, waitFor } from '@testing-library/react'; +import { requestFn } from '@openapi-qraft/react'; +import { fireEvent, screen, waitFor, within } from '@testing-library/react'; import { DocumentDesign } from './DocumentDesign'; -fdescribe('DocumentDesign', () => { +const requestFnMock = requestFn as jest.MockedFunction; + +describe('DocumentDesign', () => { test('should render document design page', () => { renderWithClient(); @@ -33,7 +36,59 @@ fdescribe('DocumentDesign', () => { }); documentTemplateList.data.forEach((template) => { - expect(screen.getAllByText(template.name)[0]).toBeInTheDocument(); + expect( + screen.getByTestId(`documentTemplate-${template.name}`) + ).toBeInTheDocument(); + }); + + const defaultTemplate = documentTemplateList.data.find( + (template) => template.is_default + ); + + expect( + within( + screen.getByTestId(`documentTemplate-${defaultTemplate?.name}`) + ).getByText('Default') + ).toBeInTheDocument(); + }); + + describe('when use selects a new template', () => { + test('it enables `Set as default` button', async () => { + renderWithClient(); + + const selectTemplateButton = await screen.findByRole('button', { + name: 'Select template', + }); + + fireEvent.click(selectTemplateButton); + + await waitFor(() => { + expect(screen.getByText('Document templates')).toBeInTheDocument(); + }); + + const notDefaultTemplate = documentTemplateList.data.find( + (template) => !template.is_default + ); + + fireEvent.click( + screen.getByTestId(`documentTemplate-${notDefaultTemplate?.name}`) + ); + + const setAsDefaultButton = screen.getByRole('button', { + name: 'Set as default', + }); + + expect(setAsDefaultButton).toBeEnabled(); + + fireEvent.click(setAsDefaultButton); + + await waitFor(() => + expect(requestFnMock.mock.lastCall?.[0].url).toBe( + '/document_templates/{document_template_id}/make_default' + ) + ); + + await waitFor(() => expect(setAsDefaultButton).toBeDisabled()); }); }); diff --git a/packages/sdk-react/src/components/documentDesign/DocumentDesign.tsx b/packages/sdk-react/src/components/documentDesign/DocumentDesign.tsx index 7da1e764d..04d779802 100644 --- a/packages/sdk-react/src/components/documentDesign/DocumentDesign.tsx +++ b/packages/sdk-react/src/components/documentDesign/DocumentDesign.tsx @@ -9,7 +9,7 @@ import { useLingui } from '@lingui/react'; import { Box, Typography, Button, Stack } from '@mui/material'; import { DocumentDesignSelection } from './components/DocumentDesignSelection/DocumentDesignSelection'; -import previewImg from './preview.svg'; +import previewImg from './preview.png'; const DocumentDesignBase = () => { const { i18n } = useLingui(); @@ -26,7 +26,7 @@ const DocumentDesignBase = () => { borderRadius: 2, border: '1px solid', borderColor: 'divider', - background: 'linear-gradient(rgb(244, 240, 254), rgb(255, 255, 255))', + background: 'linear-gradient(#F4F0FE, #FFFFFF)', display: 'flex', justifyContent: 'space-between', paddingBottom: 0, @@ -37,10 +37,10 @@ const DocumentDesignBase = () => { sx={{ justifyContent: 'space-between', paddingBottom: 2 }} > - + {t(i18n)`Set your document style`} - + {t(i18n)`Various templates for your documents`} diff --git a/packages/sdk-react/src/components/documentDesign/components/DocumentDesignSelection/DocumentDesignSelection.tsx b/packages/sdk-react/src/components/documentDesign/components/DocumentDesignSelection/DocumentDesignSelection.tsx index fc862a8fa..4b881821d 100644 --- a/packages/sdk-react/src/components/documentDesign/components/DocumentDesignSelection/DocumentDesignSelection.tsx +++ b/packages/sdk-react/src/components/documentDesign/components/DocumentDesignSelection/DocumentDesignSelection.tsx @@ -4,6 +4,7 @@ import { components } from '@/api'; import { MoniteScopedProviders } from '@/core/context/MoniteScopedProviders'; import { DialogContent, Stack, Box } from '@mui/material'; +import { useDocumentTemplatePreviewLoader } from '../../useDocumentTemplatePreviewLoader'; import { useDocumentTemplatesApi } from '../../useDocumentTemplatesApi'; import { DocumentDesignSelectionHeader } from '../DocumentDesignSelectionHeader'; import { DocumentDesignTemplatePreview } from '../DocumentDesignTemplatePreview'; @@ -24,6 +25,7 @@ const DocumentDesignSelectionBase = () => { isLoading, setDefaultTemplate, } = useDocumentTemplatesApi(); + const { getPreview } = useDocumentTemplatePreviewLoader(); const [selectedTemplate, setSelectedTemplate] = useState(); @@ -45,7 +47,10 @@ const DocumentDesignSelectionBase = () => { {selectedTemplate && ( - + )} @@ -53,6 +58,7 @@ const DocumentDesignSelectionBase = () => { templates={invoiceTemplates} selectTemplate={(template) => setSelectedTemplate(template)} selectedTemplateId={selectedTemplate?.id} + getPreview={getPreview} /> diff --git a/packages/sdk-react/src/components/documentDesign/components/DocumentDesignTemplate/DocumentDesignTemplate.tsx b/packages/sdk-react/src/components/documentDesign/components/DocumentDesignTemplate/DocumentDesignTemplate.tsx index b92d5f2e9..70a76fea2 100644 --- a/packages/sdk-react/src/components/documentDesign/components/DocumentDesignTemplate/DocumentDesignTemplate.tsx +++ b/packages/sdk-react/src/components/documentDesign/components/DocumentDesignTemplate/DocumentDesignTemplate.tsx @@ -1,5 +1,7 @@ +import { useEffect, useState } from 'react'; + import { components } from '@/api'; -import { ImageWithSkeleton } from '@/ui/imageWithSkeleton'; +import { FileViewer } from '@/ui/FileViewer'; import { classNames } from '@/utils/css-utils'; import { t } from '@lingui/macro'; import { useLingui } from '@lingui/react'; @@ -17,11 +19,11 @@ const StyledLabel = styled('label')({ border: '2px solid transparent', }, [`&:hover .${templateThumbnailClassName}`]: { - borderColor: 'rgba(86, 43, 214, 1)', + borderColor: '#562BD6', }, [`& .${selectedtemplateThumbnailClassName}`]: { - outline: '8px solid rgba(238, 235, 255, 1)', - borderColor: 'rgba(86, 43, 214, 1)', + outline: '8px solid #EEEBFF', + borderColor: '#562BD6', }, }); @@ -31,17 +33,28 @@ export interface DocumentDesignTemplateProps { template: DocumentTemplate; onSelect: () => void; isSelected: boolean; + getPreview: (id: string) => Promise; } export const DocumentDesignTemplate = ({ template, onSelect, isSelected, + getPreview, }: DocumentDesignTemplateProps) => { const { i18n } = useLingui(); + const [preview, setPreview] = useState(); + + useEffect(() => { + const waitForPreview = async () => { + setPreview(await getPreview(template.id)); + }; + + waitForPreview(); + }, [template]); return ( - + )} - + {!preview && ( + + )} + {preview && } {template.name} diff --git a/packages/sdk-react/src/components/documentDesign/components/DocumentDesignTemplatePreview/DocumentDesignTemplatePreview.tsx b/packages/sdk-react/src/components/documentDesign/components/DocumentDesignTemplatePreview/DocumentDesignTemplatePreview.tsx index 3600d0724..4d0be37e6 100644 --- a/packages/sdk-react/src/components/documentDesign/components/DocumentDesignTemplatePreview/DocumentDesignTemplatePreview.tsx +++ b/packages/sdk-react/src/components/documentDesign/components/DocumentDesignTemplatePreview/DocumentDesignTemplatePreview.tsx @@ -1,15 +1,37 @@ +import { useEffect, useState } from 'react'; + import { components } from '@/api'; -import { ImageWithSkeleton } from '@/ui/imageWithSkeleton'; -import { Box } from '@mui/material'; +import { FileViewer } from '@/ui/FileViewer'; +import { Box, Skeleton } from '@mui/material'; export interface DocumentDesignTemplatePreviewProps { template: components['schemas']['TemplateReceivableResponse']; + getPreview: (id: string) => Promise; } export const DocumentDesignTemplatePreview = ({ template, -}: DocumentDesignTemplatePreviewProps) => ( - - - -); + getPreview, +}: DocumentDesignTemplatePreviewProps) => { + const [preview, setPreview] = useState(); + + useEffect(() => { + const waitForPreview = async () => { + setPreview(await getPreview(template.id)); + }; + + waitForPreview(); + }, [template]); + + return ( + + {!preview && ( + + )} + {preview && } + + ); +}; diff --git a/packages/sdk-react/src/components/documentDesign/components/DocumentDesignTemplates/DocumentDesignTemplates.tsx b/packages/sdk-react/src/components/documentDesign/components/DocumentDesignTemplates/DocumentDesignTemplates.tsx index 9f1428ae7..5033e3370 100644 --- a/packages/sdk-react/src/components/documentDesign/components/DocumentDesignTemplates/DocumentDesignTemplates.tsx +++ b/packages/sdk-react/src/components/documentDesign/components/DocumentDesignTemplates/DocumentDesignTemplates.tsx @@ -11,12 +11,14 @@ export interface DocumentDesignTemplatesProps { templates: DocumentTemplate[]; selectTemplate: (template: DocumentTemplate) => void; selectedTemplateId?: DocumentTemplate['id']; + getPreview: (id: string) => Promise; } export const DocumentDesignTemplates = ({ templates, selectTemplate, selectedTemplateId, + getPreview, }: DocumentDesignTemplatesProps) => { const { i18n } = useLingui(); @@ -33,6 +35,7 @@ export const DocumentDesignTemplates = ({ template={template} onSelect={() => selectTemplate(template)} isSelected={template.id === selectedTemplateId} + getPreview={getPreview} /> ))} diff --git a/packages/sdk-react/src/components/documentDesign/preview.png b/packages/sdk-react/src/components/documentDesign/preview.png new file mode 100644 index 0000000000000000000000000000000000000000..5be0ae210496e5d4639492cd3aa605128a8e50c5 GIT binary patch literal 29133 zcmdRW<6|6u)NhQ&wrw}IohE7A*lDcBwl+p%HnweBjnSa7tvmfa&x?Eig!>|!$z*3U zv->^gd^CwrQIbabg#QT)3=BzDMnV+~415>Z9)W`a9yv>QrGano4l>%#U|fVQqUt}u&$6JsH})5YmA|Pe zcPgqre0FuWw~FdF?7{7-?KLFe_?%Xg+G}Xi(wrpr?N@tyajDmFRcZ6Zjo>JTo%m|8 zqMgZMs;~+B;YcJ876HTATBGZ#4V!keU8j1f zs`ga2;K@j{KPHoodIWBR<)*?XY)0CrjoVnFwQ+ZoVcCmK!6GM#;E8l@Wqc{Au~z59 zLZz%#)dDH5=E=`>e4dr2RB2YL|2|yqXz?&)*@93c)1x`Fx%K$7qxsqd22CZZzZ@3R zl#JYdy<$(++oTbKu48X)jCaRN$rRGDBazAFh4Lk_b+#{V@JLK~8F#Y<(q`u!&wlGBPsZg#2y_k-SJe2JhanGI67WQ8YET*$u zGwygJT!Vd|oDLp-GwC&qg!6Rvg!7pEw|WbqP;1kR|DP4Q6RJ|+$>nj>%p$kd)l8*8 zAyaOJ(oERreFYI+7`<9I>=;b2+xViEaFFH)wl&Ed^W&YnncF`Is)&CRXQTMwIAhunS3HEcdZ^Q5ibd^(v7-8);_c!0H&_?pbS!MHEG6LW!^6Pl zvSr0pg^0@dj5|k1|21-EJ2h!LcI~CiY8q%K_Wu^6OjsJHZ}jVHYIg!W2KDG9#5}Qi zpW@HYQN5syTwLjmHdoWPXCxi}pq@&Ry{HGQKO{CS$=$B zFp<#tU;1T`@YiY_3z(;Cr_%YA>G^j)7pjoa9Ubf>G8bbkA;j@RL>(No?VO7lY{kAou`F0F&|YS_^V zZRduJFIDTUob-1`G&wWIFHzVm@$OqMDkqR1ujwU}0s;y?F8$Kc=s{$y;n5Y<5qxqtuNV0q5V5g6uZyoPRM^etx9a+qCDDIX?d$JT2lIFM`Pr8728VIZ z%!8z7&TPEMtug~$CqG!|hJf2{$?gkRnP%$6P)w@R{+QgI^&%&3h5VsXf*_HL2m5?O zFDSOx@TTD9BAH6b`eL@==eqL)QNlcun{e9)aq=yR?_bfCdiQGYs}*Udg9$6`7KeE6 z_t7xbT4U&=h1wu%@ad0hoVjLud!UsCue)CeW)wVyZM|uXCDAc$dGkw*b{LsLvf(Bm zaL#UaddZyYHHdYbhP$V@X}5LSlVHK{H`vo~*(|99#BCK4UlGj-I_?I4ige%bXs0+v zj$I&-F?qTY3(#1Db>mg8woRYGG$n-_8IdSRV zES%#_VKN|tgh5ut;21<1Qcn8?Zp@_1wQRL;8v}=u3T}X;YAop-+9R0p*{Z_!aK_u~ z_V@%QZ)L8-L#-r0eKB)d-p+x1dEQWl`4`RG#_y}=x4EKmYvL~NqXg?Z8s$OjY&IUR zg9<%|@oT^11v=>#q@o54uy&UtN`j60cKZ)sE`wpd6C??F9kuFg!C>@xuZJ^dC~t}T zvyGB9@U-MvO9X7%znm+l^p^TAFHFGD2xQeBhX>Qm3`N>0@KD3~nM6xoji%y$wpux? zEb+iu(pm!hUws|6Flu6VuR38#{41{RUmkJm7_XW?*zPoNt_ zuhnH-sP z@H(ltFK*9=L=VU0Y*T(DUiU?HCrcAQ*Lq)``|TUpcN7Jo2VsX`ye?n6MVYpu;MA{6L2Rv67;!B(iGNnWhg3qv{QI6gCas z_zC$EDff|cLl2;@P(vj6sZt`68gQP9RD#>~%KUcV$UW+}*MCjMWKxyL_IQSmIj`1f zj1+hn&ygQZpxRFs{`dmjY4G}rcQjw(-skD(b2okcSD84a&TP!GkfVEhi5R(o>|@jR%}eJ+s~uu2w0Fk&gnpP<~ZnDzd`&X2ZEkg)!E z_DQ1sq9+WA*1Sb%JfhQ+0TD~g&pa4~l`PPDGt&B4jJRpLc8UX+f1tE*7AD!Z<6*}aJvAa#cm8!NTNC}`2 zUbb?&AGA*rP_J3cM^rjo*(!N8EplmtuCVvtIOa@6nm+Fs~u-jv2={`<+1R`hrPfzhV-p?Vo$1C5|cx9mBp{x|Ty^grAZ>N3f2-d;) z&fDX4z$5e&Af{K{{vOM8Hg-Ix3Xi5;4DoeuO`E>N63rFqvOFEy_^Ewtkr?<&HYfgA z3$1_*#|2JMz(~hM0PSVE_Ro+(?UY5%gYu(NQH99kEk#^%c|K)%52tYuSr!?y4AoDH zZd~3Um!M+oN*&iCH-}X#Tv!z1B;mK6W_VF1C~LMB*W&Lv99OPqbK3JiXWaa*PC2go zs(tC+p6}!3(9p`#RYIc1By5%|6Jd~!<4m=4U?H~M!D zqn2cOzb)d(^%cpd&ROd?cI~cDJsiBYPE8})bHuD05AHt-41G@!Iet&XJ?YWuNZsIj zgzb!*0da{2b1tD;3*UHaRBLRcXQaw(33YyjE40=~;^5`}j$2AaU67yOIQO+&gNYL- z8$D6L#C>yi`YGaJykf<oC>QO7~`R;h^c+sX|77FW6 zv0@HcrB0KICi2?AxB5Z&2(O3Jv{hFdK{k2{xAK`3}mfFo8>une_1mp z)V$~G9D~KaC*rglT7<`k?sv&ITSV(Z+Rm$kwd>W7PO|>cm>GXOhzbBY7QgT5G+J9H zSN{51UZlUlr#gh>{f*Cc&V{B7jY+>w;Y5Succaak)HqH8z1c_`?cq9l($}-i#;E#% z%*1TLHhNx@0B{hkvwd`;+u5p2a#=E0hUa}|D_>k)BI;Ad<0rl5hm(`+eHkp$ z5{0a)=}a1%3?3KDEe!(sZECBFY;lOrNGzt}uU(dSIsm0vL918O;Ws|tiM2Rxz?*B= zL>YzqZ^GiSVih3yUG5CaWf*w#l9Ituvq{AgQxptGD?TE5#1e3ezBTttQ*V5Uz;;2s z;s5YZGgtaai9w?{`#fJT6#YebBQsrXkHpXK>-*L8@*U6S^ECe|p2gi*(Oj`B!_(T< zN4hB%h4>OUQJiH5Xws^bDT6z)Upkg&?j(J$PD1fwqg}b6kj#$66qmqDuH16N`5|kS zH83TsMf{gwjEKyB%bMqd`VRU|lAWSJ+{2c8T(ETTINKq1seVDmXO@5@D4dp&XC~MD z7OOSA0iQDxsg-9R1AE0ot+=TIU)tPH%9znE7ZT$}d&R@33^oPooXnCq6q^@95esQT zLQ_<(hb9BGc`J;eOVGFzprf#@Y%RmR-UCAb_$3LCP`+Kk&n8!a${PGF*ARTA#VAoH z4#tN=YjT@2z>s(;x27gCV?Np~D_hiziDK6Uf&!Re(F(wX(t!j=Ay@itM8@F1P+VB- z)hx5^j^Y$I+oK(K5#U-e&p>L7u*CjP7RVs2u@Wb(!VW^^5iB)>h~TCG)r9?zKwt;A zkYTsl5HUnKqX81?hJq#*bW<;QKYHad8L9~0zvw(<{uUc)d%LjN=2{Lze!kJJ$+Zix zrj;6_0)Rxz1EescD&lo`V#3|i0;8nHZ0$2sIvz&7+8U)fwpLBbKk2~^&h=y|KES}Q z015vWNV1C`+Vb+u_vQ?h>d#ONfmI}0{8MdhNuv|N84|qI#LmPJ=8JzCgZi&rq0O;m z2CF#3Iw(uH+UfYG3GI5X+1_ zV{$E{G4_6aG~=?^c(SMa(%SdN?VQ0OX;!9QT6i}mE2-1*LpJaz4U;MmMwF<83Ingx zYg(TUQ&lG7;j#8lMn1Ruxi>(nLK5Dl5J3dq6eF@A&_A)I7o=dwe7@OP4)8^31kc;! zMezeBtzV0q&+AL_jo&W%i2w+V&cxeqgvDU(9FEUbW|M*LTFjCeC$E;lX%*S-XbeBi#`z`{d4}jC{4XubFP`Mg8ola;B^l{{zhjZ5+bx_mkpr|IG8;&wRP3L` zX@#R4%7_ii17iH+T-Lu?2W^r2`}-;Dd-99x;Q&g^MB;NB&*{3l)?hVX=>2qk{fFd3 z3kC_F(ri2>ng6^+{9q!z;6qvPF6lEKE{Az^7o+=zYbvMpf{aaP!ozS>qwkyN)AcyZ z*R}KclDG!zML8dS1HZRMKwpvKx_mirSEe9Ef)PJ}IIzgiqT`i~nzEX=;B$&u7%|V! zhi_~9b1#IBhDLVL`4NA6x}tcZhy&oO@h@}y?fRW0R)#ocwA{9&ThqPH=+AL86>Rioi`xb%Nc zvx3GaAYfRiky}Tj7wqBOWrQHUyy$vS3PmO=!%7xL* zhHAH6`~0+V;ak#ZNVbJz;Qhtz^fws-HX{hY%+_PQ&eMV=D88ktMSDf5`l{DH4;${u z9b{%gvqS$b7cvI(7w_ipc$URRoyPCyBU~jKi}d;xX|I3Zy^=FaQ{Skb}KN^nCCa#WMarA%iPW~R(}_k zu3=klRaFx5doDbs{24P}D%aGMiXkqTT~%KW{6#@QFmk%$%%xHx67Y(zkg{ngkvVv| z9fUHL^L|US80dl4=6X_`vvMtGkuRYnyPivEJq zB63e*%P;bBxj107-)}(X{Fbhi!*|y3cid^Vwe>H(6#tZ@d8Td;Q z@&%4+N_V?OQCDb7sZ@oA;rA=lB8W3}9L*ewYM<*p>Wi&=PZsOLtQQ+D7Mvs|{a zO(M+u5aU*nJkFx^6XTcsU;x!C3BBE_E&lKTSw$_^nnwURT2U)3KHaPTwoasxQ&C_hR zQT!1gQ>Jc43J<%x)+_FtyXdKUKmXRnu3D}{v8xd4`}TY!9<%h)hrnWa3H9T&cDMna zn^dJ-CY~H18+r!14XY@^uQN~P#pNz*onG!#nt@F}{$8h?!jQfA-&Glg63X~|g z25e|kP}(4d4y$?UyQeDsPMw=@@Lp|3@v6)Fvo$JudU`V2AdzS!g3p#^YN}oMDJCA? z;L|`8& zqjIStT#i-JGUaod=iTu?WjmcnYCs#zvgd30(4tP!kjr2smS2qmFjMy{n>SUFR)UfW z`CN~G9pT2{h=q~L2IN0$e7wKi`2o+fo*J4!+L>#MCN$=>ELLkpP%UYJ&0!q&Wi_c6 zbc&ZP+VygAX*eJ*peh+wb@?~-Rsrg)fGnq+fS^~cFL?KqZ>Flqz-DR7V>|%o@piY2 zk#JO+AkNI1!Q^Yo`E_D2sdnJ-`L2 znS1=X%s`t6v)=D{bhyZrbk2tfH zYbCVTB}Umq?K z8t5`7wvTGRw{m9XT%319dZ$?#2=cIB@auhu`T+CiGD5*)mniqnqY}UU*6xPYZ|1Q- z?Z=4@NG0(9i~v86CzQFv`*tc^R`RKVo`opkRE-c!DZjsYVGWIB^RohELJdnm7cW7_ z#x9CU4&IZI?237DyYl z>XpgGrbKcSpCNYN^e7=VNa^2iakB#o_bpd6=y~C{sbTU$XmX1JO$~Y~9!9Cb?sZ+) ziI=-A2CxLWRNC}@2%wY@IWIME<03Bm|F73HDv27r&v}QISXReo-dwTS zA?|i2OlvW10P2Ijkq=R&Dm1zqQ?1jACsB7`$7SbGAOkL%`-imJ&tyAZw48PaQH_c% z@*tY&ln8{>(f9u0s~ z&LF%BgeIJ>)PJiLkm=}x@594+&xYCD>hU^p{=oQ9`k}Gz-u@yi!CE!SM72A~|1j1K zgP3@AlRB1Oe^}N2no-Gr`;i!*xOc?wH74H4Cl?Qb01B*Yct}xq|MwM!Ea z5=uGgyv1bV4EmsJHp`sx2v07m#db7O2(#IF}M*nx2KZVB@f*RK4Ebq zF}pPcrur{VH3|l8a{4b%{i+ld9e@+aL+Es&ep8I}HBq~TwrUneQZjh18?QuDf=L_}$jwvGqvtT2gy;N_u6iL{^AYUi zq>Ikah?t4V<;(P5q$-0gURlC^LNu9Jju4t2+HC@IsfQj`vla2^kN0U=N(58I61IN)r4Je3^no?)A7WB%aUlDKyt~=tQ}VOv z>vijkJ|n}y1r0`T_#Ze=;wvbNP~YR6i_s$v3#vv`jp9N?zqbd~E>rkX#{cxgg(-^r zt`34x3aQ_c^-OxdKnX{A;?hZ`Xd5UbLdOadgD$z9E>8)2PC97jr+TeULtKjv9L#zx zzs%2~?D$Jerey#f&}HRxrS8P(fM)7Pc=?U5=*73G9|&P>^qI+!`GaPT2dCOTN;8>g zU9miMQffjT^cu`9u2GnDg+eC%4>JPBU8eFI0=u~x#S!7}H(`)!$-%w%f-hhDi7&?x z5{A8TLm81_#?c3(s9vQ`?r5<{D-OcB{jr!aod%x+gtooG6?qWxa8Mm`{czA>-ro?li_9DYn5~kXXPK!trKv`RBj-fbOT@>+o{xJ{q-hd^-J^Hz zE{6}_%ve9}$3rhqQI&99Ti3b1{ZV~l2;aJ|$WPb9y!+WwjPo7W7Mc0EgyZe;DKQ)%nts6iF#TUa}J}>)?A1|rMN&qU+ zxHk7F4iXPT8IQoAap^JMa6X)}5t-iXd_ljz8S36jY5us0eSPq|IC5%tyI2B`yF(`C zqa2PUN`-_)K6WG1T-xfih(k^))MAU%Uw$>mW3}Iec)DTmOWf+Xg2asNNExB{MCKv@MdmzzlgW*A|i+E!l#u_WlEhj85$dz zH8I_M4Y>wv6Lz!X1_EBpued?IpGD!9Rx#^!kqKKp2P@WjXMb#1#E#c}xA@Ha2P!pE zQqnrLi*elDygg5XsbTj|iE)vGD8sGMNCwZzh(j#JR;6w;@5vYBl^UzWgk`A*kH0fU zR!a>Lgsz26M*Y-J(|%ivgtNtp_~Ruxex+9OK6WQeQP@oJTsF&c zvE?RVZtRY8rc`IoUY+8NybDOO(qnbnV&3ma*fTPPZ_Yzs)yu+6qt1E<1}nR>S~K03{+ z3aO3iSp5Ri`&%{T5o&jXRAyajzz<9j^m-VpGh;|{(5%v<;<8x)$*L8X^(=Mj;?U_9 zyQt`-5c7JfzZBkNR(-2i%CX93s4@COee}C4t0Gc|Niq^UK0Mq(&>k>jTR!v!A6!qk zM{GMidHS-Lfh<6LBo+hDj(r!9Kv0*8c~`Q-?45fG%tuq>f7YQ*5&qdR#}U@yA+03c zLx%vl)%pP<6m*nB46TS1umd5XAUH$LOO>kAi7~hLCVo31I*`lp9Z( za&E6eQ)fl3JnpSa|DZhkwbD#tGgec|+oO$fv~NgOziR>ror|}`lsTzoipjVPq}6bm|EUoDjR z`}EJkYwL=6Isu1#oToVp$M-HX;XE9zph(hGt#q$Jvy3$l4+~IYU|)dDz-Vb=iF$jb zc75AHQMNm^LU;gsBv6_QCLfE=(|q$ghF0ses;dolRBUYgu00asL9g=3^hH6Joe%4k ztEiCgM9mLq{f(k>1SzO<$)7?nogQ*SDQ2!!YsH{T%!uQ!{?N!&6K|MZbal#=IUh{T zzipfP#~ zv3;pmtl$LV5-9|dta5rVMY+QOI85d4!uMT#GW%qf_%1xfJebAx)cc-l`u0A4y{yk$ z=%pB6of6)`^_HJqNpmE>%m%CAb8(?s;*@4;3RRvfZD{l$f0dzHrPo^3rYCpbFXW`h zRCi7t@DX84%}kG`HIVHCJJp>GO*d<90IP#qTvjlp9FzjR!xm2dWbA1wbU^0)1>mpC9=2;u$pD)? z;(S$6xCsO^eEE)WgFEk$OQstP*%W6wZXav3I}lN4K8bH(S4o)#m=aG478_}4xLid` zmAcDTw&j$Zoa%AOH-FckhKklJV5%M}sg3-LZCG)uBvf!KoLc9WGyQf?&7Qp9p<|*q z-O0@oB!LgT$p0&38$D?;9*6k|AhmJsqOlY3I2dMbu_zN$DU8HlpyHdC0#OH9fQ2eu z%;W_z>b0oIVlhQp0pdW?Xd=xC-?YFD02L*VJVC7QnbSfq3Zo4`m!8UO)Usaql{Wyt zgX36Cma?`t5$*MGp8RP_9cH#(pqJQedX4FN2Yq=s7L>2|voBG#W2hZ*Nx9FBZ&1&be$b1TC4PA8`BAjU(@egUV z9>}1&{DNi6%z%HvnFZVUj5HyWVq%tB*l2_K;_0NSZ@W-8@EA1hf9S~Lk?=>7O4W)= zI3y|7P29{Gdy-=KsTeB)W4VBY)e;XB)a;;q3eSxf2bW_BSD@HHBg3~f1@F05mu~%g zLGY5oQCQcKC@f^9ToH4T{jp>-07%MdSk^&(7U=>soEq<&Np3moe{_%WTbp`n03%t$ z>eK}U_;nl%!Ymf8iU_gSDI1kA99|+D3pEPYTJU$Wcfhlf0}u@7Z%EZcXhZGr2EZ$m z>$9v!Wz+e7b1-Q(0wQZ;)Vah^7RVcpCHd=r^F#`Vin>s#%ZJ?S6BMw`s~_7d z9eOQT9(-p+W;lhHk5JL%^BJrI##0&KDQm^&*ty5 zFSx`SUzlhLef4ZWo!v{cCCG39A9Pn^H`;w(dHzRK2y#?)>hsht1fZ%HW~Z`~ni zElW5P++&72=Q768)3T|l<=F4M{_7cWl(FYKfVltb-eaqtoiAXIJeuGH&@WQNm9*nH~s zx_#f&F*c81<3Mcq-yONZ*k{;S(JLTXd2i}2Q6i+Iq^wooAOe;nFFGIefl`i6;C|T_ z(26zbt3W!cXD+r053=HSI|blNZ+n06 zd(?Ur0kuVaR8?s_9HAlba<&QA1rN;cbvusJY%h;QIQQlX_9k;LM%i)oJT^*%F|fT% zvznS=oz!DzmntT_tJB^f#z9i6z0!Re3LPeIYPHT`));BYYOS~tW({GQZuZ!SvI{^s~nlp>h=VWR^LnJTz2bG{F6jv)j$^^Atha z=HO_T^TkS{$a!*#cC_8;?C4BU~cd{zun8fbVtp85N%>ZTzD&>HlvBZ1x*~^#Q+Ds#Uv-wRYQ+iAbsh72NYw0rW6JkG*(l^bAx^yLGLQ zke>bXj+?3A3jivl3|tO-Gt@GB78OEu&Bdi2Kr%p4!9Ly;5YEnM!!psRywHBhZ?UFC zj1>0W_I-MddcU5RCYkjhBZE#(BPZ|U^5g77!zqOMl22yus&o3Ogp2$U)Gq%#^3C15 zT96bsPtm!%BLs?!1+`qWI$kgK^70%awXV%DF##rWOpuc`83_h;zY zkSQVIcrK|tqA|A>#2algZq33YIAkcIG1M@atfKXYnP56(t316zYek^e4MW4Bx69K>);T#za9THZgHi+p9`Ki#<5{hkczskS5qtE%TN78+}-0r zWi)X+PxAG7`9&(c1rKX^U$pKXJP@UnX0ipH)l;*nE&ZwG=JoXE9iMBn#ZeKD%_(Wq zFPkhDmVGc%45vPw!r zYHmkbOg|B|9kqqBQak9G&hP=37BKGi#?ss3+pNHCg3{RH@gJDz$TgTK(;tB(@i&}A zax1ach7+aFOohE*w=0JRd@*dQvRI-a;K77)my_FMb7hhUR1IMyo+6FbF!Tybh}$vv zTrSjy(C7zHR1GJ9&Odf<3uk@DrHnhrpj%wgG+WRkZPNS-UlNiV!tAI+9Yj?uKarUx z$}6NQRonv3Iq<|(>xl9a^r5}b&1>&J~PysOaob8J$6rXOa0MD+LE!{J(!XUc~R+_1r@6dC)H z7z-&%DqI5;fv|wAR&@Je1OtU2M7o3BAa=-lR#iFm_cyqSQ8mR8tzzo6j};V#^S3iKrCEK~zh_s_hJ2XzD4-RHZi4Jx8|kH=ASzs#{l z5dk?jO$J3GbJIUXj~lO7co_m;U{JIIr;bc8AR0-Ar!j)ZQjvr<=0PM5d5*fytuBSe z-)1XfFW$k;eHZ27m@c+5yiuXQfywzE3EXv(Ozq>x}xN;9XnX5`LEWoOQ!T zjGA;bj8a@L*wZxCYFk6pG|YqfTOiP?boQ34F?*J9y8H5^8xt8m_BLzl3G-M3Igu_( z!&>m~f>;>!-Ts}0Uw$!YBG81!mJmrC_=jmf98l%spVnl3EHBd>*zH6%zAhCkm;Llu&al>;T9fgV z%47F^qJtI6UnnH3LK>Tzo4Rv>wCQppgp)sHB34Pda|RA<<8~;d+8#X133*0vYvtt| z&}oP6BL`sh1z*N-IB8E$>|tzF$V@O4X~T1eC+kc{*xaVuc)*gzIo4wZ|m z$%>psaqMwG_$Hp$`JjxY`$N;cJPtny4HJZ!;{iTCLYk!e8ob*~6m5yT3nlMZ3%^4Q9{r1eE7#R%d&XtTowH zepyTNy4t|N^O@O@zF%am>-q#e>uLX|!m-tP->E7pnf~uh2gn%bOeR%Ca4LQ38`LEl zL(fgGeHCngnZ-KC6r0-c>%di_BihUnm?(|0O1qOJ-qTP{q5BCnw_+}IR>S@4+uL5j zmN0{dlSNf#w;_b;9{2|$B-b}ew5A1obY+XhKeVN+zm!0|f zDsz^`W|HDdLzCvEAZRrw70&amm0k{3sHxvjX^(!5dN3r@#2T!guzT2>Tj@rKOi4B9 z^66Sx^B0=k3{54J6A!WSt3U0TU-z8>kwAn)_TQFCmUl+hQ5S2Iz?PO6SPV7fFB(r;f^#Lm9-XBA8+$=R zbamjCL2u=3nR^R;F2#TD9G`#RJ|F0T1VK zPwtdUy&6WS%@(7=PYF=_Wa{J{`(x?yo_D8JZ1RO2M0_q1z^F`SD1AwSyC4v)(K&}< z(qf>?>~{DPs*puNg6Pa*H9^c}q=%B$e8ur`TBSc%sKM3wBOT2Os{$*{Eg>@>Mo4Z} znw;J8Krz2A-TwGoUh_IG9e=V&E|~=8HyP~@=L%tJ<%!a7-F#f}#DR^Z9V<%nc{Rd5 z8eVsQsefj2?L^;0ZZc%?H)&3$BxOAazsj_^o|OeL7cRA`NpsyLQulSHpNHeBk?v_0 z$(hj|KoytHo69KCh_L9iXyJ23sw;*{Ar*EQ4pDv(fFrY0Ph;AQS<%3W_@f9pGKD>?@>3r&vi8+@Zt2BYEI z9;-WgDn?hO!CDGGs${E6p-3_$)NSimR>sKv9$kP zC&@wO%t&zxNoKpX=`&ZMjnP;(Ggfcs%||^=RBRP2U*;*vW0RFLg&P9$*O;JL?z0)` zF!4_4O3{O2&^k$3b(iIM{?hC=*ZvhF9j8&Fm`R_Cf!i+m8!WOF4i%-IQ;k*QjyaBr9ymu67h~Kb8B*lH@?3C%f;8RsB4F3ThE+NlH66HB00ad?ian~ zLm5Qzky|syH3u5Y3P9kibUZtpQ1o;=Cs7#IPGcV2{_ME}Tv5U}Ip??H+a^J$*1UlR z2QW@ey*@gBW#WuKnPuJwngxl1Oe|LmBc6|*?~W>0aXiNSt7M^fUqT83P#% z*{q}_f@w~1<~asLwfiWh024hvFZONzJYMb-2;NuXqr${3mb5Al03M$D>3E)kyBiALG>=TE3$O28=gsZhS9%hBTE-1d5Sg$SUb|+;!^^N> zmgNC0`0$WJP=1J1;(tQ4-TmZJ-#266;US^m-bFhluvWeAxUWMKGmvT5cjFU057Wgc zln|AUgA+Tv8N|;J_u#mL>E+0v)d{BP;ZmfchjvJaFRCS@Bq60RJ}vaH@{9U*;X#ex zpqr2@G2Ugx#mNBa^$|14Lf9BwRmq#&Ef$?mrqb$kYlMR46$kh1r0kbFEQNL~)JAZz zZz5Ie&i9kvf%mLegN@A$1#A6?-)7KktPZaVmn_AXePs zW$C)9$mhah(NKVf42FZzBlhVeR&vN)MW-GEiII86+~^vZ^dpgL;X7ua*Y0>hMTM76 zM%!c04dBT8n2*WtqTIXw!t*aR=JI>@_WAWof6!mZX>X*ZcxN)h%7tv4&3{+ZlmPMf zVirlbD3KzCJ@)sm`0ShT73#jPH5pAM*a`H6My!R>fFv0v_?AuxrHkew=^^{6;iV}^ zg4KkN`Z-BVN7;fG zp9M9%-$~)Hm*RJ1FNT;jeIZ&9mI07n(MBrW=Rmd=MG?P7s|X}cQ@MmTu`d1r)G6Z3y=a>3EtF5(u!MDhvxG77vR9 z>6~{(6W?S#OK!bid}Q+H7cb>zv)MGkuzi`?DTotJ=9(Io6QVj2ib`c(JPgdmj@(Xk z3bx0~VHPxieR z3iF}cnX9Ox)syDV1nN%htDD4f*;bj6n+lGv zYVun1KLI9R4t_eZ1l&g(uP^8&LstT>HvTYhg`tC*@6+Dobi7B$^ezgB!_3>qB5G+I z7TEv}(lSA%r8buMKJxd{k*(A=qv^KDCD5>lWmXTovJe<+d_8K=5uDV4En?!;>m*<1 z5H}+NNF2*sIWV;Sg0syC7>!j{ofZwt}?_jP3?G(a#e>& zc^;)y!-&H3BI!IdZCT0;1ds$N8f8|z2ace6uhDLDbFHZFNArtzs|RmSgDkzJskYu| zsn(PcyA?mSJrk!E?~}ib5ySLLPY9z1PVkKm$n#-te%`!@yS zVQv}h66d~B=d)9Zln1J-&g=C!I(Eq3T@EfEH%iz=+UGhP<4;b_qaz;3qT#PnA29@= zsP0b5(XIFzL%EH@Q>K-lF_fnW-Y!!!+>UeMvZJGd-d>J`)uP-3Kd%bj|2DZ^(zUlz z1BYk-i+AJTd5vX~42c(;KItf>!&D%@vC=%bllQ5 z0Ym`Y0T4_tP1pgbfD37f4hH1ZVUAzb*BnyWi6T@YGc5R3vbt0Zpq3gZGPvab#qDtR z|0SVS&G~#~njThds_6p6wk;dfV~p|MS8yaCOVK-kbB^AYg9fni8H zkmSTb$Wltu*$%|fhcOUU6OWJ-VYsPq~LN5f}A{4IR+ zd9O)K0L1ex^mm*vN{+PKmd`_}6rxp$bc5}h1~fr1`c(Xjr~{_NF$pcFK^4|vP;qo_ z00^S@nxC9pj9MWrw^XTXgKQZ^HH`5$%$FtLpBG^FPg2x~fKB5-g7qalkU&zH1d%<$ zdE>mjr0IC{%bv6RpT^EAs*NsO!?+ZO;uc)dh2n(bUZA+UySuwfad$25#T^P1 zcR#~_*112|$s!YGW@pbU&!^F&Wmb@~zy%3sNg4u`nwxr&?n^R`-)qvX&qKiGS_BCC z+N}4Y6F<D;0o!vux=Y19clg-iDo{|+(-c2)*s{IPMxI=&)kuGJ<>kN=SB744JW z`}c8?%Ah8~OTjL|rv83w(t{UQl~**~$u-i=mZ}ZOCKPh0cE*X`Lv9d&R)GlC4nQCN z*aM#nD+_@GHH>!*MdcBabY}M240qp8gl1m%dF%>7;s=`05dsYYxo^v$CM+QLcw9T^ zeRSMB>mQ|(*d~&EDu&Vff%sq|LlEiUv0~&lmuB)w%xRxsL3nDC5cLri7D)b+ql9RV z9@f3x{l@wn<{wpwJkwwlA%&SNlBPi&Z+0(g_?-#+o4k zW_nObAr<@0G&dnrJ`lcVe>jDQr8bu_U&dWQa+@1_ZWs{3-6ESrBHa)_ z;f<&1sTh!1_7}%WfGD6UaqYyrx1K%9)RfPu%mr+TU4&us3V8@h z2l!rhK=Jhy5kV{EBRE@v^>Q&$7 zty80KWpmNW(rx9*lv?RQQc6i5{UmvVq(E9QOU*r}%hKwS=22N(5P|T4z*b34} zhovsJp?o1{SY88Y8!$luuaz|av z(u6mLWmIT&F5rIwM)~AsV#w#PA_Zd9hH(=9lqJC#x==x6MxCM)X5C`hc@4Y(lif-(*#y2fSApfxxQ;fIoSUwEnaLmQoh{Z*slqCB8N#KPmKFZK4K^#yP%W zb%Fvng5Tec+=MB7(it$B48f3>TUsJ^o>(m!F_H@L`FTjiE3XHN5)-kzmv7t-eHTdG z*T+IoQkC+Lx8A3@-y9XXj)*Bdxy@ufYr;X9YVQ);6S2kR^kNnLc%G4?{u)t)Mnw8B zwo4^?o3O<8en4AlWJ6a>&yP%jq93Fz{AU7VNZ6xjr+Ng95SK7NcbHC+j6()FfTR&p z>p$6rOe=&Y1yy*inG&!})#LzDep+ilwad%n4Ns&B^;>#vxR{aXV}bm5I;W!N)SO09 zJ4M$L4y_Uc5~AqbzwntTBHS)S#r;xw887RV*%?_%sFGD4K}-XnjvqIw=xZm{Q%F zMneEfj{u&ipGlg;Q-LeAIG=|;8k^=T$P^A~IBt%L?Hik>);UXL6+bd&jOF$wgAgS8 zaEg_1ftbOQ*U&DtXn}{wUpChq449{2@Q98C-g%(N9|4_!sjAR*la^gn4>-}98oezM z+M}$2x92+@PqikV9@~q!LLQsRtPX{cjN-{NAaQQAblDt4D;Z4^zwxp)d4#(CH0tf8 z8r5=VvG-fmuABdgL^g>b-t+11@DAwRA3NbNfC~ydw)>?u+pIgVWRj1c<BNr6;W=T7V6=LaOZsVXowC9m`8jNx8~$s-U(RS{YTAU_ia zYwUQnJbpzO-xlJ$)1Y&FADvvxn$am-d*{p57dO!9QDFa~icOM>qe=l1CC#}W{{z@3ZC5#)`@m-@o&;mU9LtjwhLLTJ zaE}@IS8jAtoAh#bN0ZfI*Z1hvWOgJ_urhq03z0kad_QFRe*5?yQSYJA=Pr} zaHvJ&zWO9~EAj>jJ#YN2-Gv4FYci8At?n%;P1nl}(qrfI+1gzpP_(>U!qt^Y(MHWH ze0mQ&thsA{6?n_*0=jQ9oexeW49+61E-op^Bwyqdcnsh$PVXwc7LkLlVFqJz9zr~g zEBk%eE(o8#B>F@NLUh>}2&IwKb0EWX8lnxe((1k5B2*~K8$K1>?zMfyM?QpXBgzFO zkOCv>@(#KRL+Yw33?l$VG*6)pELGgPliLAhW8l&!mwPP--R0q?0h@^%#Oo%mF5O8KF=f(aa+I z_X3aF`Ub7QT(u2z(tP^e(PB}#xB3)jon<33zV8W@*Kd6j=2+9UD1dXDNp?wv zmIg>}&**I#4F_Fk?bNCnmW4pb_K1GH(=#~XXPSA|s_SlmeSU(|nNsSPLac?6MBe1r zeNvR>3@j1R)u*6FJ|IEafC^c^(i@0m{!cvc!`}6iQnBmK+2^l7YScFSo#?S@{hG#p z{K+&bqwK}+aw1QqNx`ag=T)8eSOsW=7Sz~3u!a4|F}W0(a0B*JJ~4fj2Y~?}uz$;* zedkxL8BY4#hUF&v_rmL9&nLhd{Ql!OMQ4fO5EvwF%mCHItTc;9l8yel1FRzrdnrnzd8VU5L>viBJetL z1JRy&-z+3KO77kmvBH2vfL=K{W{f3vt9&3}klKVjFJ}q0K<*qnAnID~9Cw@~B!vp^ z*uup8y|1MI=?62+G#iRAlwUCdm+;fcYD-;O7nw#8hLXGST_gw@Pk(n5vBL0S zvB1I60|4LaD z)j24A6Nr{e<!r`uiLbFyDygbW{AS;ecE23)24t*#8)6}R-hcq z(LiAmJol54CIAY-{p}UH^+d4qIn4c_Z!i1Jtq*Yrh0^VYfWHa|h96TOg6}80i22xh z?lM1XgO9M)xT;3#4_S>bwkdf|V=O(#bn>6ARCoK-_LQ$>eq+*8ck3fw)tf_;j$JD*qgKW11CZPq5~L=_ zW&A#s)A2lU(B*JwCvQj*=DAdhUE5=oY$*=&Dq>o=3+9S$nGn}lC06VdyLmzBfi;wx z3P}mhQ~`Ok#C(P%dgT=xyGvuYq-(hx>9YCYyOHuxAnI_Z`VDVB)rG2~IJIUYn8k>hiBP9HDa`tDm~$M| ze+-oTb`g#YVxl7XKL?``C;i%?l6rA-TqCI+DVa886Cu!d`n59AQg@K>Z+{}4X*``H zaX>cN!XjY^Ao;MTAQ}ALLWorpVuS$7Kc|b3R{?sAOhmal1lKoY`Wl^1YOCxZirA`E_8>~3 zV=Y#(QMJU-5?@i-ND%WvB=}cfcboFTztU2iQ^T4-aup=cf`7bqf&cBT^9}0U`8R6kJ9mIZl>hY*S|BiY~nYk>k%8}C1WP<2p3P=mu1TIpGk!4A=sadixkC&4A@5Cnrrz9;^3wIRGcZ_n&fe&WdaaX< zV14aLT;Z>Xt7h$wREldTyWPJ^sm!)<^)@SNpJQ}ePuL=UY-Y8c6#)E6gJwqr3*l%X zm?D~cumlO+hBnu^y8-jsC9(|(pv1j9{T8q6&@Og9;d+nPhlo{BbTP!pGzeS10nr1{ zF@zJF+2EM!6X-LOiHy|UFQds!i`!_LD67RK^V6rx61;w3s@SN%VL0?@CYJ^5D{H+gy{P&3syOmCjl>8jFo%fZM~2apV}I z>K7Hk>3>tdLnaX-EEsh=GgKXn`5Q&yH}~Li1<^_02>={lv(j% zXUamqX8W@{D($_YBuxkcnz{jnX{*7&hcvA{Uz{%EQ8md|TSLk3&I%0aOg1F*yTk55 zAtAiNWWGkDNwk$|0#NQiSatW$w4v5l7|cE*o%vY2gE%}f=irtX3YdQLjyhtNMHNUG z&|ili;4s`tn}#ygjNY&E(I7^s4i6CtgvF|y*`gLFuxtW?au=O?X$CV~qt%+$>)kqh z8hO$Xq9y_4*@VA+4<_F6*h-Fa;m`RJ$RVZa*v|)U3&e)nrmDl)g4tP|IDvH&t$-NG zza|-ZZ@BY}!RTTlC3;op(eg?}7&cAoOp5UrNHmumSjYYYigU9eD4aoh=4x0}azE2l zb#EG=>*-}8E-^1XLuUgZ;#doQ%YK0D0=O6!9o@ATXWnnxpFhTMLswT1jO+C~{W6hD z=a6q87>+07NdggMSz%TTwZ=45@1Y>>Y-IDhH?k@Tmb}uoz7hIyap$L>J*qx#9iBFS)tlaZ?EKkOrE~q~XrVMejha@GXbA4W zi>~5OaUcVyRRb~6FlFzQby8K-^eD*8#i=A`h{HvIEC6drk*l(<0hyFV=9arM{vn3c zkMGqkD!sk}&sk1<^}(*4G83Y9UMC#s)K!1IboN)7ah+~+0g$atOVs9)C#uA=XXzb& z=h^Loo4ylQ>Pd+I%F}u|i<_nEOW5V&q)TA8nvr6^OszAKojhGjWI+5P$~(X_b`+4{w`FMBKFK#>V*6nP~`S40$P!su&>C2;IW)bfc zqQcT@XZqh8I%36hbozq=$IG=F@u@O!X)RU(W2rg8l^v>SsL;~^L9Duf{Q?nx7ME;@ z6k?OL(;c!tWRIBcvL+18kB@un)5?^xrP=|8qn1=h+EBnwwvdTs^U*@1Qwf@h{1y8< z4ap}GX132shC(-cbgnG(&bvS;5GpkC=6;+n$Lso^?1`PSBVh}QvLu*S|z^?GNEA`pbW>u<#04Nf}*$O zcJ_{>=r{$F?thbmCGP;pDA!uhRma*k9fd0t5NENHB-4r>&+lG^a*+Z4BC#`cq!3L3 z)DqUs>%|bMCAb?JjgQQx^P%V+k?_T23@;O{Cw@PO-j8Q*RkEyP9PkI45LxPU^3FkK zQS`QDE0sbfin_gg;oSH+YaBKUY+J8{%5WaX2&zC-_@n58_|!}?nKT=Y1y??{a4T}g z$x4&6K!_4g42NwT2~{4DHK$>@w95x=VVMX8hIAB(@?b3!z9H|LDVfOv53{q7nS1Y4 zh6B;GvSW6eb+9yiHp(&<8gD?<#|aL+XZ-#Vb+7GE>Q8n4Prec|@brpit60NPPu=Wd zcwwwN1BjI9tWk92 zAGA)%{e14(n-2zic$!%|H&21Xno%$I?f;?=5UkSOC$75dpv<45zBggw7y;Y_Kt)L_ zn)Z>5zyrNw1uQDeI$}r((ECT7;S@X)kbBvF`h?)8;CoC@A>ffu`7Z=tlbdkExkx1$ zgIpIxS46)_eCrDyc80rJv2K&6Qp_Sc(6-x+5Q1?1$$;*XE#Qd>MLk2HmP>0aR4$3$ zJl_z0(g~1f5`0)bnTbVkMBwAiT$xL!1bdQ@LQ@ei1VkoD{6h7!VP+L+hGjRkuCs=p zxiyig=Y;aqfPc%mV-@|4&Sqj&Pumy?x7Xy0Gj8`I`(|ov_S?86;gk)D*_k=Erp?{> zyV@p9X#f~W8qho@2IR%sP1cqBYvsPfMjMA8u)Hjw%Hgg3%3~=PkhT%6;hGuEvO~`$ z3L6T@`B?^@P<4H~-TXE(6y=uHc?GIahO$+%E#6t&F_>k@3 zAVFbKI~3-VO0vOduAqTAd_3^B+-RkJvw`^s4ntq*JP9dp@&TYa`K~}NLhYh$ZQ%~~k9kO8L;4vGiiqs8Z(JZ^-NUnpY|0W2x9C0mhBQe+={ zG@3D)_ja17$x|Icd}A*$R7PT9cw(?4B7d^}(td)<6|E1Q4fF8S4RHh@vyYkelrYdz z6UDi?hayKryr+nHtD*R0_Y=67p!0B_&oCbXAhQ*bL&?d6{&Z5Y9E>@Ew1-JK;r(Ej z*$7w;pi>nyxRiDXN+P2mx;YThceqj}F0KwI=K(2B833lqq1LOBqM#_nTEGZj@@sow zlM143wgH^n;5cl^uL5W3n_O(tMOUGX5v7DE@g7EO=EG=E)XKz_n0C}u5O#ghqa1u5 zMjX8mn_N?2AO$JRFMHXZ_G9RzPXh_av?5tP4+28rRzgKF)CVw-PuElsrSbM)AoUTj zqo+e!4Kba+^<-$kvj{8fXI1Mdu3{FLB?vtv4J=x?>KrpwJSotc7R1gHR z*6}(Rj@1dnp=D_Lz`_flL^L#JVY$(wGUr-4o^Fr1Gi_&E7L8?+=N=@ZiP46WC#fh! z!$-$A=SnrW|7GRRoGyWGE_w%$?6&)9@8kl0lQf-)fIz1jm$DW+{h{&7HF}WTrvlu9 z6U9`qp*!$!5ZCk(B1#pO~u=$!6N8`{n`3#hlD zg1f+O9TOIn(8?H!%_kQl?(swND?97Br|tkCtmrz7*;xOu__tph95RASp8v;T!SF&aPkM(8r7pVYd93;YSr_3lmIR? zsl&b0BuViL9oI&CRE1jGmA@3A;TA!S{4Qaqi}(~MpWfJN%rxxgoH?kN1>$u+Nkk#! zJib|}H*VW6y?Xld3(JoFm;0muAe83+SZeHxJrnLw%pmnaZ&_Tb9Y9$Ffbl9{vYPZl zvA7HpGbUQDy72#nEEOJ1u+~rOq$idy(3w|mG>bFp6fivtBf{Z)*4)bKdimmhO78ic zT0XUKl5x>HkvHkDBcVf!6|d8Xm~%G3SYTjjyB!AT9GI;KRx2bx9r!Cgc83DuZO)Sy zyHEw+TRX1y8vA;^ar*9L@}!kv@~*?vu2)^;9uT*>h)ayBus^K(D3>CU5$daQTrPwE z1$BaF;ziGJVDwO6uNs{}5qj`GUOK>7t6|Rt4 z95e$Xw-wy|GhCc@saAaR9iD4R|jRvat zxrM$o@z8RzSgNXX#=RR&N$dUE&V#b`V{+XSQT4_T7FckRiOsf0*$KX&;-M38mR0Yj zf|!bCPx^#}YMi@o#ms&8|MpDh=x6PDS_M?&L^uOD~q_RZz4_kIt|8x~i& z{L(f{f{VSekS$>?&kI$eMZK*2NJ0fretBy@=M64-5Iq99yT8580up81Obh7qW+X8IEnBrAJn!Z|N&BUC#9fHll5DA#`} zlrc{(?9PFe3WY$G3I}#+w1Y$ncND|ndxtp*(;;|ydti}_w-7jNhww1KULM3oAgE6) z@D*pi^Pcv20y1e(zIyA_0FRY?LL|qNOc*uB!Vbb#blGemhHm#8P|~L~Tn5byL)t;f zkVF-bnHGtzo6W%@Q3-C+bokhB5rGVrdF@c^H4E%NR+9Ql_X@d-nQ~Ug_QYhNj}4ns zY%{<}H5d$psXK_Y;!Hsu_O$`#*2%mqOzEP}^oLYb+G%NX;7Mb-2{%u-$vU%m|7tR7yI}V>FDe+3xa7Vzvkd zJjJ>0B3dMuN)B`onrS}=65ehFla`^`1H49ppZ;f8Z$JWet_vRlGa-U;<<{^wfR-lKJ)ax|UhT4uU!Ks+u6ZF{kV62=D2_rh+JaVEdF#(QP$Jw(;&uYx zxo>t0b22rd8U(L#uO0n;$RCOtA++iI3>YjkLT&vy_%Q6vhM7-6AsT~e(C}zSwj}lk zBW*1J&ykblM}qOFx)}vr)wf$RxEWj0mMXxei;pDaGah{vc@nGAR6>3|Kpf ze6HKd4|t2#ur8+pFaKnh>RrP*D1iD6nP!)MJYlk^@p~iP9dMd>5 zsk`-ED+Go!jP{3O6@KqH10FF%b2$Iy+!XM#8B9goz>!+PCcjtV0{!I&=BL8Dsas*&zRuLv|^^)0bQ36DN7S@c&{k zXGU3kq~Jot?iOJox%WFM{%F4JsC$jM)|bdmjPN+bVGo`K2MrDBo3Jr};HFCeh9`gn zF=zfXM}^}uB?9p11if6F>=mP)-axx4M2={_>l}~Uhh!eO*WK9Q-4`^$q%VcCvMY_1 znIgZyc>iul{AFp+0 zL6X$7U{^d0`4cR6BcLa|K5e`m$zC58Xa9yL^~l+Hx;v}B0M2#$akaxaG~DioOiUA(|2F9wLCmYhZaQ&u(zLN)NC@OJt>bUrUhld_tG#aM4;L!aBk)*6oZ5P91|#rg zhI@R9dYbKP-Zo$0k2}}yd6pVFwt54@3q%4hYYpb<#-#0@`wzsR#xT~TTPTPAStF#el-eT*yqovOQ9tV(M>!B3aXNs}2qlHvo4%rM z?dazKT<=@Lm%n#tkiRCeP9H#{P@_^4L`!>ZXM-b^P$Jr5E-9U+4VkJtKD>+hr)gp; zVLum=iQTe?kHxj4LYGQ+erj?u0=J8{{Cuf#i$<+ODlip6!Y?!j-ctDMy)%ocUajZl z2(|IiT&{sXu2 zg}4Cp&oak2>)y-v^Kz5zdGR$Dqw{)3_KWo@m+Mv_?yjQuWBjW^Mv#b>A|UuVq$~1W zi1l6rh)Z_i{04F{Jp+?BGm;cbh-lVFKjCCTnI!e)!)ZEaMvi4 z-Qj`d>2JU&f0@pPt)yToyJe$FDOj_i9stYyy;!r*%VGd?9k$!B$utmfZ{?=)az3tC z*_-@bu|6);np(2A<-Uth0xatJE9=w2d_8htxYdM#y82Pu(--eoDE_35i2?MJ3MZX5 z2RoTDH}+%$eT`-A^U0^CV}wW;0*iF0Z~W?2S89ng`fl+{<^#iKqpmb>2fPK1LO5jdtpmy{M$kC5A_?w z6+^d$???(uKgC5yi{`!iTBH1NfH{KI2t9N{YxhjtdB@0|fL3*`8ghA#EgAqPl1f>u zv^?T>j+j*~Q%TaJ7@tX=5(01#RlbTbbVl%@AWxBIbld=(k{0sJ%T3{etqIQe^nPhI zj(PUmJKtM=NUCKWcc2j@JGKT^X;j)sU5@*rt>@?Vu$R17EckMWeFdQJs#YXqCF7~5 zkl3s}3a>~iNwy$-q3RG#Y}qX8jToRcpqOVxk6g<-AV7)Iyhez8s#2D zD*|1%PBo8_AERgq4Ve#q!8mWs1@Nxl=^xWx1RwwAtIPR&OuHcd={EIX-^>mIbCY=V zpmRNyY1S3-op(bMuzODO-~APO{Z}_hr-U(u{dX3X3LTYs=}9Hz7}A>$2L=Y+Zz-mi zrPzKABSno{@KdJ~3{Aa95y)fnP6AJ#Kgra)B3!i>7jG+m1O4-pPxA}MACgasdyLy~ ze^5K=U|3HfuWBM{{#C+EUy04a^Cp5=H`R(;fO&oRZ)lrte_*BT`^JFjixAS{3Sw0v HdVc={^%`Fu literal 0 HcmV?d00001 diff --git a/packages/sdk-react/src/components/documentDesign/preview.svg b/packages/sdk-react/src/components/documentDesign/preview.svg deleted file mode 100644 index 0347d0377..000000000 --- a/packages/sdk-react/src/components/documentDesign/preview.svg +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - - - - - - - - - diff --git a/packages/sdk-react/src/components/documentDesign/useDocumentTemplatePreviewLoader.ts b/packages/sdk-react/src/components/documentDesign/useDocumentTemplatePreviewLoader.ts new file mode 100644 index 000000000..80dc34c29 --- /dev/null +++ b/packages/sdk-react/src/components/documentDesign/useDocumentTemplatePreviewLoader.ts @@ -0,0 +1,93 @@ +import { components } from '@/api'; +import { Services } from '@/api/services'; +import { useMoniteContext, FetchToken } from '@/core/context/MoniteContext'; + +type FetchPdfPreviewParams = { + apiUrl: string; + api: Services; + entityId: string; + version: string; + id: string; + token: string; +}; + +const getTokenCaller = (fetchToken: FetchToken): (() => Promise) => { + let expiresAt: number; + let token: string; + let promise: null | Promise<{ access_token: string; expires_in: number }>; + + return async () => { + const now = Date.now(); + + if (!promise && (!expiresAt || expiresAt < now)) { + promise = fetchToken(); + } + + if (promise) { + const { access_token, expires_in } = await promise; + + expiresAt = now + expires_in; + token = access_token; + + promise = null; + } + + return token; + }; +}; + +const pdfPreveiwStore: Record = {}; + +const getPdfPreview = async ({ + apiUrl, + api, + entityId, + version, + id, + token, +}: FetchPdfPreviewParams): Promise => { + if (!pdfPreveiwStore[id]) { + const path = + api.documentTemplates.getDocumentTemplatesIdPreview.schema.url.replace( + /{(.*?)}/g, + id + ); + const response = await fetch(`${apiUrl}${path}`, { + headers: { + Authorization: `Bearer ${token}`, + 'x-monite-entity-id': entityId, + 'x-monite-version': version, + }, + }); + + const blob = await response.blob(); + pdfPreveiwStore[id] = URL.createObjectURL(blob); + } + + return pdfPreveiwStore[id]; +}; + +export const useDocumentTemplatePreviewLoader = () => { + const { api, fetchToken, apiUrl, entityId, version } = useMoniteContext(); + const tokenCaller = getTokenCaller(fetchToken); + + const getPreview = async ( + id: components['schemas']['TemplateReceivableResponse']['id'] + ) => { + const token = await tokenCaller(); + const preview = await getPdfPreview({ + api, + apiUrl, + entityId, + version, + id, + token, + }); + + return preview; + }; + + return { + getPreview, + }; +}; diff --git a/packages/sdk-react/src/components/documentDesign/useDocumentTemplatesApi.ts b/packages/sdk-react/src/components/documentDesign/useDocumentTemplatesApi.ts index 10a862fa7..ca94a2d93 100644 --- a/packages/sdk-react/src/components/documentDesign/useDocumentTemplatesApi.ts +++ b/packages/sdk-react/src/components/documentDesign/useDocumentTemplatesApi.ts @@ -3,6 +3,7 @@ import { toast } from 'react-hot-toast'; import { components } from '@/api'; import { useMoniteContext } from '@/core/context/MoniteContext'; +import { getAPIErrorMessage } from '@/core/utils/getAPIErrorMessage'; import { i18n } from '@lingui/core'; import { t } from '@lingui/macro'; @@ -13,8 +14,8 @@ export const useDocumentTemplatesApi = () => { const [invoiceTemplates, defaultInvoiceTemplate] = useMemo(() => { const filtered = - documentTemplates?.data?.filter( - (template) => template.document_type === 'invoice' + documentTemplates?.data?.filter((template) => + ['invoice', 'receivable'].includes(template.document_type) ) || []; return [filtered, filtered.find((template) => template.is_default)]; @@ -48,6 +49,9 @@ export const useDocumentTemplatesApi = () => { t(i18n)`${name} document template has been set as the default.` ); }, + onError: (error) => { + toast.error(getAPIErrorMessage(i18n, error)); + }, } ); }; diff --git a/packages/sdk-react/src/core/context/MoniteContext.tsx b/packages/sdk-react/src/core/context/MoniteContext.tsx index 52bcc2c75..baff2dea7 100644 --- a/packages/sdk-react/src/core/context/MoniteContext.tsx +++ b/packages/sdk-react/src/core/context/MoniteContext.tsx @@ -35,6 +35,12 @@ interface MoniteContextBaseValue { dateFnsLocale: DateFnsLocale; } +export type FetchToken = () => Promise<{ + access_token: string; + expires_in: number; + token_type: string; +}>; + export interface MoniteContextValue extends MoniteContextBaseValue, CreateMoniteAPIClientResult { @@ -45,11 +51,7 @@ export interface MoniteContextValue apiUrl: string; theme: Theme; componentSettings: ComponentSettings; - fetchToken: () => Promise<{ - access_token: string; - expires_in: number; - token_type: string; - }>; + fetchToken: FetchToken; } /** diff --git a/packages/sdk-react/src/core/i18n/locales/en/messages.po b/packages/sdk-react/src/core/i18n/locales/en/messages.po index 561620ea2..d7c0fe907 100644 --- a/packages/sdk-react/src/core/i18n/locales/en/messages.po +++ b/packages/sdk-react/src/core/i18n/locales/en/messages.po @@ -156,6 +156,10 @@ msgstr "{issueDate}" msgid "{item}" msgstr "{item}" +#: src/components/documentDesign/useDocumentTemplatesApi.ts:49 +msgid "{name} document template has been set as the default." +msgstr "{name} document template has been set as the default." + #: src/ui/DueDateCell/DueDateCell.tsx:45 msgid "{overdueDays} {0} overdue" msgstr "{overdueDays} {0} overdue" @@ -1564,6 +1568,11 @@ msgstr "Close" msgid "Close approval policy details" msgstr "Close approval policy details" +#: src/components/documentDesign/components/DocumentDesignSelectionHeader/DocumentDesignSelectionHeader.tsx:26 +#: src/components/documentDesign/components/DocumentDesignSelectionHeader/DocumentDesignSelectionHeader.tsx:27 +msgid "Close document design selection" +msgstr "Close document design selection" + #: src/components/receivables/InvoiceDetails/ExistingInvoiceDetails/ExistingReceivableDetails.tsx:180 #: src/components/receivables/InvoiceDetails/InvoiceError/InvoiceError.tsx:35 msgid "Close invoice details" @@ -2303,6 +2312,10 @@ msgstr "Declined" msgid "default" msgstr "default" +#: src/components/documentDesign/components/DocumentDesignTemplate/DocumentDesignTemplate.tsx:60 +msgid "Default" +msgstr "Default" + #: src/components/receivables/getCommonStatusLabel.ts:39 msgid "Defaulted" msgstr "Defaulted" @@ -2549,6 +2562,15 @@ msgstr "Djiboutian Franc" msgid "Doctors" msgstr "Doctors" +#: src/components/documentDesign/components/DocumentDesignSelectionHeader/DocumentDesignSelectionHeader.tsx:34 +#: src/components/documentDesign/DocumentDesign.tsx:22 +msgid "Document design" +msgstr "Document design" + +#: src/components/documentDesign/components/DocumentDesignTemplates/DocumentDesignTemplates.tsx:25 +msgid "Document templates" +msgstr "Document templates" + #: src/core/utils/countries.ts:79 msgid "Dominica" msgstr "Dominica" @@ -6351,6 +6373,10 @@ msgstr "Select invoices you would like to finance and send them for review." msgid "Select someone" msgstr "Select someone" +#: src/components/documentDesign/DocumentDesign.tsx:51 +msgid "Select template" +msgstr "Select template" + #: src/components/receivables/InvoicesTable/useInvoiceRowActionMenuCell.tsx:202 msgctxt "InvoicesTableRowActionMenu" msgid "Send" @@ -6398,6 +6424,10 @@ msgstr "Services" msgid "Set a recurrence period to issue invoices automatically" msgstr "Set a recurrence period to issue invoices automatically" +#: src/components/documentDesign/components/DocumentDesignSelectionHeader/DocumentDesignSelectionHeader.tsx:41 +msgid "Set as default" +msgstr "Set as default" + #: src/components/receivables/InvoiceDetails/CreateReceivable/sections/components/Billing/FullfillmentSummary.tsx:99 msgid "Set by payment term" msgstr "Set by payment term" @@ -6419,6 +6449,10 @@ msgstr "Set reminders" msgid "Set this counterpart as:" msgstr "Set this counterpart as:" +#: src/components/documentDesign/DocumentDesign.tsx:41 +msgid "Set your document style" +msgstr "Set your document style" + #: src/components/receivables/InvoiceDetails/CreateReceivable/sections/components/PaymentTerms/PaymentTermsForm.tsx:140 msgid "Settings" msgstr "Settings" @@ -6961,6 +6995,10 @@ msgstr "The review for the persons has been completed. You can proceed to the ne msgid "The start date for the recurrence shouldn't be in the past" msgstr "The start date for the recurrence shouldn't be in the past" +#: src/components/documentDesign/components/DocumentDesignTemplates/DocumentDesignTemplates.tsx:26 +msgid "The template you set as the default will apply to all documents you issue in the future." +msgstr "The template you set as the default will apply to all documents you issue in the future." + #: src/components/onboarding/dicts/mccCodes.ts:1310 msgid "Theatrical Ticket Agencies" msgstr "Theatrical Ticket Agencies" @@ -7647,6 +7685,10 @@ msgstr "Vanuatu Vatu" msgid "Variety Stores" msgstr "Variety Stores" +#: src/components/documentDesign/DocumentDesign.tsx:44 +msgid "Various templates for your documents" +msgstr "Various templates for your documents" + #: src/components/payables/PayableDetails/PayableLineItemsForm/PayableLineItemsForm.tsx:129 #: src/components/receivables/InvoiceDetails/CreateReceivable/sections/ItemsSection.tsx:261 #: src/components/receivables/InvoiceDetails/CreateReceivable/validation.ts:53 diff --git a/packages/sdk-react/src/mocks/documentTemplates/documentTemplatesFixtures.ts b/packages/sdk-react/src/mocks/documentTemplates/documentTemplatesFixtures.ts index 43cf7c07a..37d740a87 100644 --- a/packages/sdk-react/src/mocks/documentTemplates/documentTemplatesFixtures.ts +++ b/packages/sdk-react/src/mocks/documentTemplates/documentTemplatesFixtures.ts @@ -14,19 +14,6 @@ export const generateDocumentTemplate = (): TemplateReceivableResponse => ({ name: faker.word.noun(), template: '', template_type: 'source_object', - preview: { - url: faker.image.url(), - id: faker.string.uuid(), - created_at: faker.date.past().toISOString(), - file_type: 'image', - md5: '', - mimetype: '', - name: '', - region: '', - size: 100, - previews: [], - pages: [], - }, }); export const generateDocumentTemplateList = diff --git a/packages/sdk-react/src/mocks/fileMock.ts b/packages/sdk-react/src/mocks/fileMock.ts new file mode 100644 index 000000000..86059f362 --- /dev/null +++ b/packages/sdk-react/src/mocks/fileMock.ts @@ -0,0 +1 @@ +module.exports = 'test-file-stub'; diff --git a/packages/sdk-react/src/ui/imageWithSkeleton/imageWithSkeleton.tsx b/packages/sdk-react/src/ui/imageWithSkeleton/imageWithSkeleton.tsx index 7a802c3e4..99a4b0c07 100644 --- a/packages/sdk-react/src/ui/imageWithSkeleton/imageWithSkeleton.tsx +++ b/packages/sdk-react/src/ui/imageWithSkeleton/imageWithSkeleton.tsx @@ -5,6 +5,7 @@ import { Skeleton, styled } from '@mui/material'; export interface ImageWithSkeletonProps { url?: string; alt?: string; + skeletonHeight?: number | string; } const StyledImg = styled('img')({ @@ -12,7 +13,11 @@ const StyledImg = styled('img')({ height: '100%', }); -export const ImageWithSkeleton = ({ url, alt }: ImageWithSkeletonProps) => { +export const ImageWithSkeleton = ({ + url, + alt, + skeletonHeight = '100%', +}: ImageWithSkeletonProps) => { const [isImgLoaded, setIsImgLoaded] = useState(false); return ( @@ -23,7 +28,7 @@ export const ImageWithSkeleton = ({ url, alt }: ImageWithSkeletonProps) => { {!isImgLoaded && ( )} diff --git a/packages/sdk-react/typings.d.ts b/packages/sdk-react/typings.d.ts index 98f50ac74..41c05d073 100644 --- a/packages/sdk-react/typings.d.ts +++ b/packages/sdk-react/typings.d.ts @@ -12,3 +12,8 @@ declare module '*.pdf' { const content: any; export default content; } + +declare module '*.png' { + const content: any; + export default content; +} \ No newline at end of file