From e95825a0009e1660bf0dfc2ad3fbaa50e5bc7271 Mon Sep 17 00:00:00 2001 From: sjaanus Date: Mon, 25 Jul 2022 10:14:02 +0000 Subject: [PATCH 1/5] Feat/grouping fixes (#1146) * Grouping fix grid not showing user names * Remove deprecated access endpoints * Manual prettier * Revert user role update --- frontend/.gitignore | 1 + .../ProjectAccessAssign.tsx | 2 +- .../ProjectAccessTable/ProjectAccessTable.tsx | 2 +- .../actions/useProjectApi/useProjectApi.ts | 18 ---------- .../useProjectAccess/useProjectAccess.ts | 36 +++++++++---------- 5 files changed, 19 insertions(+), 40 deletions(-) diff --git a/frontend/.gitignore b/frontend/.gitignore index 7d5390b6df..10bbc1c1fa 100644 --- a/frontend/.gitignore +++ b/frontend/.gitignore @@ -49,6 +49,7 @@ build .DS_Store +cypress/downloads/* cypress/videos/* cypress/downloads/* cypress/screenshots/* diff --git a/frontend/src/component/project/ProjectAccess/ProjectAccessAssign/ProjectAccessAssign.tsx b/frontend/src/component/project/ProjectAccess/ProjectAccessAssign/ProjectAccessAssign.tsx index 73ff6c6fb3..5f033a65da 100644 --- a/frontend/src/component/project/ProjectAccess/ProjectAccessAssign/ProjectAccessAssign.tsx +++ b/frontend/src/component/project/ProjectAccess/ProjectAccessAssign/ProjectAccessAssign.tsx @@ -1,4 +1,4 @@ -import { FormEvent, useEffect, useMemo, useState } from 'react'; +import React, { FormEvent, useEffect, useMemo, useState } from 'react'; import { Autocomplete, Button, diff --git a/frontend/src/component/project/ProjectAccess/ProjectAccessTable/ProjectAccessTable.tsx b/frontend/src/component/project/ProjectAccess/ProjectAccessTable/ProjectAccessTable.tsx index edb500bb84..fd75a7224a 100644 --- a/frontend/src/component/project/ProjectAccess/ProjectAccessTable/ProjectAccessTable.tsx +++ b/frontend/src/component/project/ProjectAccess/ProjectAccessTable/ProjectAccessTable.tsx @@ -32,6 +32,7 @@ import { useRequiredPathParam } from 'hooks/useRequiredPathParam'; import { IUser } from 'interfaces/user'; import { IGroup } from 'interfaces/group'; import { LinkCell } from 'component/common/Table/cells/LinkCell/LinkCell'; +import { mapGroupUsers } from '../../../../hooks/api/getters/useGroup/useGroup'; const StyledAvatar = styled(Avatar)(({ theme }) => ({ width: theme.spacing(4), @@ -339,7 +340,6 @@ export const ProjectAccessTable: VFC = () => { setRemoveOpen(false); setSelectedRow(undefined); }; - return ( { } }; - const addUserToRole = async ( - projectId: string, - roleId: number, - userId: number - ) => { - const path = `api/admin/projects/${projectId}/users/${userId}/roles/${roleId}`; - const req = createRequest(path, { method: 'POST' }); - - try { - const res = await makeRequest(req.caller, req.id); - - return res; - } catch (e) { - throw e; - } - }; - const addAccessToProject = async ( projectId: string, roleId: number, @@ -226,7 +209,6 @@ const useProjectApi = () => { deleteProject, addEnvironmentToProject, removeEnvironmentFromProject, - addUserToRole, addAccessToProject, removeUserFromRole, removeGroupFromRole, diff --git a/frontend/src/hooks/api/getters/useProjectAccess/useProjectAccess.ts b/frontend/src/hooks/api/getters/useProjectAccess/useProjectAccess.ts index a3ceea8a32..9d67f266eb 100644 --- a/frontend/src/hooks/api/getters/useProjectAccess/useProjectAccess.ts +++ b/frontend/src/hooks/api/getters/useProjectAccess/useProjectAccess.ts @@ -1,10 +1,12 @@ import useSWR, { mutate, SWRConfiguration } from 'swr'; -import { useState, useEffect } from 'react'; +import { useState, useEffect, useMemo } from 'react'; import { formatApiPath } from 'utils/formatPath'; import handleErrorResponses from '../httpErrorResponseHandler'; import { IProjectRole } from 'interfaces/role'; import { IGroup } from 'interfaces/group'; import { IUser } from 'interfaces/user'; +import { useGroups } from '../useGroups/useGroups'; +import { mapGroupUsers } from '../useGroup/useGroup'; export enum ENTITY_TYPE { USER = 'USERS', @@ -45,11 +47,7 @@ const useProjectAccess = ( const CACHE_KEY = `api/admin/projects/${projectId}/users`; - const { data, error } = useSWR( - CACHE_KEY, - fetcher, - options - ); + const { data, error } = useSWR(CACHE_KEY, fetcher, options); const [loading, setLoading] = useState(!error && !data); @@ -61,21 +59,19 @@ const useProjectAccess = ( setLoading(!error && !data); }, [data, error]); - // TODO: Remove this and replace `mockData` back for `data` @79. This mocks what a group looks like when returned along with the access. - // const { groups } = useGroups(); - // const mockData = useMemo( - // () => ({ - // ...data, - // groups: groups?.map(group => ({ - // ...group, - // roleId: 4, - // })) as IProjectAccessGroup[], - // }), - // [data, groups] - // ); - + let access: IProjectAccessOutput = data + ? { + roles: data.roles, + users: data.users, + groups: + data?.groups.map((group: any) => ({ + ...group, + users: mapGroupUsers(group.users ?? []), + })) ?? [], + } + : { roles: [], users: [], groups: [] }; return { - access: data ? data : { roles: [], users: [], groups: [] }, + access: access, error, loading, refetchProjectAccess, From 3a811b66d66da7650501d7f397284985c33158c2 Mon Sep 17 00:00:00 2001 From: sighphyre Date: Mon, 25 Jul 2022 12:42:16 +0200 Subject: [PATCH 2/5] 4.14.0-beta.5 --- frontend/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/package.json b/frontend/package.json index aa8f747e68..49da7840e7 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,7 +1,7 @@ { "name": "unleash-frontend", "description": "unleash your features", - "version": "4.14.0-beta.4", + "version": "4.14.0-beta.5", "keywords": [ "unleash", "feature toggle", From d4f46eaf2e87a1d8cd8cf6bda4f4998e2405bb0f Mon Sep 17 00:00:00 2001 From: Fredrik Strand Oseberg Date: Mon, 25 Jul 2022 16:01:58 +0200 Subject: [PATCH 3/5] feat: lazy load playground (#1145) * feat: lazy load playground * fix: app.tsx * fix: add suspense * fix: update snapshot --- frontend/src/component/App.tsx | 57 ++++++++++--------- .../__snapshots__/routes.test.tsx.snap | 9 ++- frontend/src/component/menu/routes.ts | 4 +- .../playground/Playground/LazyPlayground.tsx | 3 + .../playground/Playground/Playground.tsx | 2 + 5 files changed, 45 insertions(+), 30 deletions(-) create mode 100644 frontend/src/component/playground/Playground/LazyPlayground.tsx diff --git a/frontend/src/component/App.tsx b/frontend/src/component/App.tsx index 3313833635..62c4d55233 100644 --- a/frontend/src/component/App.tsx +++ b/frontend/src/component/App.tsx @@ -14,6 +14,7 @@ import { SplashPageRedirect } from 'component/splash/SplashPageRedirect/SplashPa import { useStyles } from './App.styles'; import { usePlausibleTracker } from 'hooks/usePlausibleTracker'; import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig'; +import { Suspense } from 'react'; export const App = () => { const { classes: styles } = useStyles(); @@ -30,37 +31,39 @@ export const App = () => { return ( - } - elseShow={ -
- - - - {availableRoutes.map(route => ( + }> + } + elseShow={ +
+ + + + {availableRoutes.map(route => ( + + } + /> + ))} + } /> - ))} - - } - /> - } /> - - - - -
- } - /> + } /> +
+ + +
+
+ } + /> +
); }; diff --git a/frontend/src/component/menu/__tests__/__snapshots__/routes.test.tsx.snap b/frontend/src/component/menu/__tests__/__snapshots__/routes.test.tsx.snap index aa97b286ac..914c091b0e 100644 --- a/frontend/src/component/menu/__tests__/__snapshots__/routes.test.tsx.snap +++ b/frontend/src/component/menu/__tests__/__snapshots__/routes.test.tsx.snap @@ -120,7 +120,14 @@ exports[`returns all baseRoutes 1`] = ` "type": "protected", }, { - "component": [Function], + "component": { + "$$typeof": Symbol(react.lazy), + "_init": [Function], + "_payload": { + "_result": [Function], + "_status": -1, + }, + }, "hidden": false, "menu": { "mobile": true, diff --git a/frontend/src/component/menu/routes.ts b/frontend/src/component/menu/routes.ts index c9b9debd2b..70a1ec1af7 100644 --- a/frontend/src/component/menu/routes.ts +++ b/frontend/src/component/menu/routes.ts @@ -53,10 +53,10 @@ import { SegmentTable } from 'component/segments/SegmentTable/SegmentTable'; import FlaggedBillingRedirect from 'component/admin/billing/FlaggedBillingRedirect/FlaggedBillingRedirect'; import { FeaturesArchiveTable } from '../archive/FeaturesArchiveTable'; import { Billing } from 'component/admin/billing/Billing'; -import { Playground } from 'component/playground/Playground/Playground'; import { Group } from 'component/admin/groups/Group/Group'; import { CreateGroup } from 'component/admin/groups/CreateGroup/CreateGroup'; import { EditGroup } from 'component/admin/groups/EditGroup/EditGroup'; +import { LazyPlayground } from 'component/playground/Playground/LazyPlayground'; export const routes: IRoute[] = [ // Splash @@ -182,7 +182,7 @@ export const routes: IRoute[] = [ { path: '/playground', title: 'Playground', - component: Playground, + component: LazyPlayground, hidden: false, type: 'protected', menu: { mobile: true }, diff --git a/frontend/src/component/playground/Playground/LazyPlayground.tsx b/frontend/src/component/playground/Playground/LazyPlayground.tsx new file mode 100644 index 0000000000..41ea00f4b3 --- /dev/null +++ b/frontend/src/component/playground/Playground/LazyPlayground.tsx @@ -0,0 +1,3 @@ +import { lazy } from 'react'; + +export const LazyPlayground = lazy(() => import('./Playground')); diff --git a/frontend/src/component/playground/Playground/Playground.tsx b/frontend/src/component/playground/Playground/Playground.tsx index 2f6943abdd..50c8bb72bd 100644 --- a/frontend/src/component/playground/Playground/Playground.tsx +++ b/frontend/src/component/playground/Playground/Playground.tsx @@ -212,3 +212,5 @@ export const Playground: VFC<{}> = () => {
); }; + +export default Playground; From 23b9cbaf0358bcaedd2c4a44b3c8deddad965c81 Mon Sep 17 00:00:00 2001 From: Fredrik Strand Oseberg Date: Tue, 26 Jul 2022 08:58:57 +0200 Subject: [PATCH 4/5] fix: truncator (#1148) * fix: truncator * Update src/component/common/StringTruncator/StringTruncator.tsx Co-authored-by: Thomas Heartman Co-authored-by: Thomas Heartman --- .../src/component/common/StringTruncator/StringTruncator.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/component/common/StringTruncator/StringTruncator.tsx b/frontend/src/component/common/StringTruncator/StringTruncator.tsx index a797c743ca..74205701f8 100644 --- a/frontend/src/component/common/StringTruncator/StringTruncator.tsx +++ b/frontend/src/component/common/StringTruncator/StringTruncator.tsx @@ -17,7 +17,7 @@ const StringTruncator = ({ }: IStringTruncatorProps) => { return ( maxLength} + condition={(text?.length ?? 0) > maxLength} show={ Date: Tue, 26 Jul 2022 08:50:27 +0100 Subject: [PATCH 5/5] fix: pro flow stripe integration refinements (#1149) --- .../src/component/admin/billing/Billing.tsx | 20 ++++++++++++++++--- .../BillingInformation/BillingInformation.tsx | 4 ++-- .../__snapshots__/routes.test.tsx.snap | 2 +- frontend/src/component/menu/routes.ts | 2 +- .../useInstanceStatus/useInstanceStatus.ts | 6 ++++++ 5 files changed, 27 insertions(+), 7 deletions(-) diff --git a/frontend/src/component/admin/billing/Billing.tsx b/frontend/src/component/admin/billing/Billing.tsx index c57975f5a1..2e82f5d8aa 100644 --- a/frontend/src/component/admin/billing/Billing.tsx +++ b/frontend/src/component/admin/billing/Billing.tsx @@ -1,6 +1,6 @@ import AdminMenu from '../menu/AdminMenu'; import { PageContent } from 'component/common/PageContent/PageContent'; -import { useContext } from 'react'; +import { useContext, useEffect } from 'react'; import { ADMIN } from 'component/providers/AccessProvider/permissions'; import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; import AccessContext from 'contexts/AccessContext'; @@ -12,14 +12,28 @@ import { BillingHistory } from './BillingHistory/BillingHistory'; import useInvoices from 'hooks/api/getters/useInvoices/useInvoices'; export const Billing = () => { - const { instanceStatus, isBilling } = useInstanceStatus(); + const { + instanceStatus, + isBilling, + refetchInstanceStatus, + refresh, + loading, + } = useInstanceStatus(); const { invoices } = useInvoices(); const { hasAccess } = useContext(AccessContext); + useEffect(() => { + const hardRefresh = async () => { + await refresh(); + refetchInstanceStatus(); + }; + hardRefresh(); + }, [refetchInstanceStatus, refresh]); + return (
- + = ({ return ( - Billing Information + Billing information = ({ {inactive ? 'Once we have received your billing information we will upgrade your trial within 1 business day' - : 'These changes may take up to 1 business day and they will be visible on your next invoice'} + : 'Update your credit card and business information and change which email address we send invoices to'} diff --git a/frontend/src/component/menu/__tests__/__snapshots__/routes.test.tsx.snap b/frontend/src/component/menu/__tests__/__snapshots__/routes.test.tsx.snap index 914c091b0e..9a49a194d8 100644 --- a/frontend/src/component/menu/__tests__/__snapshots__/routes.test.tsx.snap +++ b/frontend/src/component/menu/__tests__/__snapshots__/routes.test.tsx.snap @@ -480,7 +480,7 @@ exports[`returns all baseRoutes 1`] = ` }, "parent": "/admin", "path": "/admin-invoices", - "title": "Invoices", + "title": "Billing & invoices", "type": "protected", }, { diff --git a/frontend/src/component/menu/routes.ts b/frontend/src/component/menu/routes.ts index 70a1ec1af7..06ba81a1c8 100644 --- a/frontend/src/component/menu/routes.ts +++ b/frontend/src/component/menu/routes.ts @@ -518,7 +518,7 @@ export const routes: IRoute[] = [ { path: '/admin-invoices', parent: '/admin', - title: 'Invoices', + title: 'Billing & invoices', component: FlaggedBillingRedirect, type: 'protected', menu: { adminSettings: true, isEnterprise: true }, diff --git a/frontend/src/hooks/api/getters/useInstanceStatus/useInstanceStatus.ts b/frontend/src/hooks/api/getters/useInstanceStatus/useInstanceStatus.ts index 48e0279a0e..e483cfe02b 100644 --- a/frontend/src/hooks/api/getters/useInstanceStatus/useInstanceStatus.ts +++ b/frontend/src/hooks/api/getters/useInstanceStatus/useInstanceStatus.ts @@ -7,6 +7,7 @@ import { useEffect } from 'react'; export interface IUseInstanceStatusOutput { instanceStatus?: IInstanceStatus; refetchInstanceStatus: () => void; + refresh: () => Promise; isBilling: boolean; loading: boolean; error?: Error; @@ -33,9 +34,14 @@ export const useInstanceStatus = (): IUseInstanceStatusOutput => { InstancePlan.TEAM, ]; + const refresh = async (): Promise => { + await fetch(formatApiPath('api/instance/refresh')); + }; + return { instanceStatus: data, refetchInstanceStatus: refetch, + refresh, isBilling: billingPlans.includes(data?.plan ?? InstancePlan.UNKNOWN), loading, error,