From c765a03e9fde5156fde444f87a36bc92fb428782 Mon Sep 17 00:00:00 2001 From: Gabriel Henriques Date: Tue, 19 May 2020 17:05:33 -0300 Subject: [PATCH] WIP --- client/admin/apps/AppsRoute.js | 29 ++++++ client/admin/apps/MarketplacePage.js | 48 ++++++++++ client/admin/apps/MarketplaceTable.js | 130 ++++++++++++++++++++++++++ client/admin/routes.js | 4 + 4 files changed, 211 insertions(+) create mode 100644 client/admin/apps/AppsRoute.js create mode 100644 client/admin/apps/MarketplacePage.js create mode 100644 client/admin/apps/MarketplaceTable.js diff --git a/client/admin/apps/AppsRoute.js b/client/admin/apps/AppsRoute.js new file mode 100644 index 000000000000..1fe4b493d82b --- /dev/null +++ b/client/admin/apps/AppsRoute.js @@ -0,0 +1,29 @@ +import React, { useState, useEffect } from 'react'; +import { Box } from '@rocket.chat/fuselage'; + +import { Apps } from '../../../app/apps/client/orchestrator'; +import { usePermission } from '../../contexts/AuthorizationContext'; +import { useTranslation } from '../../contexts/TranslationContext'; +import NotAuthorizedPage from '../NotAuthorizedPage'; +import MarketplacePage from './MarketplacePage'; + +export default function AppsRoute() { + const t = useTranslation(); + + const canViewAppsAndMarketplace = usePermission('manage-apps'); + const [isEnabled, setEnabled] = useState(false); + + useEffect(() => { + (async () => setEnabled(await Apps.isEnabled()))(); + }, []); + + if (!canViewAppsAndMarketplace) { + return ; + } + + if (!isEnabled) { + return {t('Apps_disabled')}; + } + + return ; +} diff --git a/client/admin/apps/MarketplacePage.js b/client/admin/apps/MarketplacePage.js new file mode 100644 index 000000000000..ff1edc6fd7f9 --- /dev/null +++ b/client/admin/apps/MarketplacePage.js @@ -0,0 +1,48 @@ +import { Button, ButtonGroup, Icon, Tabs } from '@rocket.chat/fuselage'; +import React, { useEffect, useCallback } from 'react'; + +import Page from '../../components/basic/Page'; +import { Apps } from '../../../app/apps/client/orchestrator'; +import { useTranslation } from '../../contexts/TranslationContext'; +import { useRoute, useRouteParameter } from '../../contexts/RouterContext'; +import { useMethod } from '../../contexts/ServerContext'; +import MarketplaceTable from './MarketplaceTable'; + +function MarketplacePage() { + const t = useTranslation(); + + const cloudRouter = useRoute('cloud'); + + // const handleNewButtonClick = useCallback(() => { + // router.push({ context: 'new', type: 'incoming' }); + // }, []); + + // const context = useRouteParameter('context'); + // useEffect(() => { + // if (!context) { + // router.push({ context: 'webhook-incoming' }); + // } + // }, [context]); + + const getLoggedInCloud = useMethod('cloud:checkUserLoggedIn'); + const isLoggedInCloud = getLoggedInCloud(); + + useEffect(() => { + (async () => console.log(await Apps.getAppsFromMarketplace()))(); + }, []); + + return + + {isLoggedInCloud && + + } + + + {/* */} + + ; +} + +export default MarketplacePage; diff --git a/client/admin/apps/MarketplaceTable.js b/client/admin/apps/MarketplaceTable.js new file mode 100644 index 000000000000..2766cf1f6b6a --- /dev/null +++ b/client/admin/apps/MarketplaceTable.js @@ -0,0 +1,130 @@ +import { Box, Table, TextInput, Icon, Chip } from '@rocket.chat/fuselage'; +import { useDebouncedValue, useResizeObserver } from '@rocket.chat/fuselage-hooks'; +import React, { useMemo, useCallback, useState, useEffect } from 'react'; + +import { GenericTable, Th } from '../../../app/ui/client/components/GenericTable'; +import { useTranslation } from '../../contexts/TranslationContext'; +import { useRoute } from '../../contexts/RouterContext'; +import { useEndpointDataExperimental } from '../../hooks/useEndpointDataExperimental'; +import { useFormatDateAndTime } from '../../hooks/useFormatDateAndTime'; +import { Apps } from '../../../app/apps/client/orchestrator'; +import { formatPricingPlan } from '../../../app/apps/client/admin/helpers'; + + +// const style = { whiteSpace: 'nowrap', textOverflow: 'ellipsis', overflow: 'hidden' }; + +const FilterByText = React.memo(({ setFilter, ...props }) => { + const t = useTranslation(); + + const [text, setText] = useState(''); + + const handleChange = useCallback((event) => setText(event.currentTarget.value), []); + + useEffect(() => { + setFilter({ text }); + }, [text]); + + return e.preventDefault(), [])} display='flex' flexDirection='column' {...props}> + } onChange={handleChange} value={text} /> + ; +}); + +const useResizeInlineBreakpoint = (sizes = [], debounceDelay = 0) => { + const { ref, borderBoxSize } = useResizeObserver({ debounceDelay }); + const inlineSize = borderBoxSize ? borderBoxSize.inlineSize : 0; + sizes = useMemo(() => sizes.map((current) => (inlineSize ? inlineSize > current : true)), [inlineSize]); + return [ref, ...sizes]; +}; + +const formatPrice = (purchaseType, pricingPlans, price) => { + if (purchaseType === 'subscription') { + if (!pricingPlans || !Array.isArray(pricingPlans) || pricingPlans.length === 0) { + return; + } + + return formatPricingPlan(pricingPlans[0]); + } + + if (price > 0) { + return formatPrice(price); + } + + return 'Free'; +}; + +export function MarketplaceTable({ type }) { + const t = useTranslation(); + const formatDateAndTime = useFormatDateAndTime(); + const [ref, isBig] = useResizeInlineBreakpoint([700], 200); + + const [data, setData] = useState({}); + + useEffect(() => { + (async () => { + const appsData = await Promise.all([Apps.getAppsFromMarketplace(), Apps.getApps()]); + setData(await Apps.getAppsFromMarketplace()) + })(); + }, []); + + const [params, setParams] = useState({ text: '', current: 0, itemsPerPage: 25 }); + const [sort, setSort] = useState(['name', 'asc']); + + const debouncedText = useDebouncedValue(params.text, 500); + const debouncedSort = useDebouncedValue(sort, 500); + + const filteredData = useMemo(() => { + const filteredValues = Object.values(data).filter(debouncedSort).sort((a, b) => (a.name > b.name ? 1 : -1)); + return debouncedSort[1] === 'asc' ? filteredValues : filteredValues.reverse(); + }, [debouncedText, debouncedSort, params.current, params.itemsPerPage, JSON.stringify(data)]); + + const router = useRoute('admin-integrations'); + + const onClick = (_id, type) => () => router.push({ + context: 'edit', + type: type === 'webhook-incoming' ? 'incoming' : 'outgoing', + id: _id, + }); + + const onHeaderClick = useCallback((id) => { + const [sortBy, sortDirection] = sort; + + if (sortBy === id) { + setSort([id, sortDirection === 'asc' ? 'desc' : 'asc']); + return; + } + setSort([id, 'asc']); + }, [sort]); + + const header = useMemo(() => [ + {t('Name')}, + {t('Details')}, + {t('Price')}, + isBig && {t('Status')}, + + ].filter(Boolean), [sort, isBig]); + + const renderRow = useCallback(({ author, name, id, description, categories, purchaseType, pricingPlans, price, purchased, }) => { + const handler = useMemo(() => onClick(id, type), []); + return + + avatar + + {name} + {`${ t('By') } ${ author.name }`} + + + + + {description} + {categories && {categories.map((current) => {current})}} + + + {formatPrice(purchaseType, pricingPlans, price)} + {isBig && {}} + ; + }, []); + + return ; +} + +export default MarketplaceTable; diff --git a/client/admin/routes.js b/client/admin/routes.js index 6ede81d1ef1d..a2228c36cfee 100644 --- a/client/admin/routes.js +++ b/client/admin/routes.js @@ -37,6 +37,10 @@ registerAdminRoute('/custom-sounds/:context?/:id?', { lazyRouteComponent: () => import('./customSounds/AdminSoundsRoute'), }); +registerAdminRoute('/appsNew/:context?/:id?', { + name: 'admin-apps', + lazyRouteComponent: () => import('./apps/AppsRoute'), +}); registerAdminRoute('/info', { name: 'admin-info',