diff --git a/.eslintrc.js b/.eslintrc.js index 2434047bc..dbf4d7ad9 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -81,6 +81,7 @@ module.exports = { 'no-unused-expressions': 'off', 'no-use-before-define': 'off', 'no-useless-constructor': 'off', + 'no-console': ['warn', { allow: ['warn', 'error'] }], 'padding-line-between-statements': [ 'error', { blankLine: 'always', next: 'return', prev: '*' }, diff --git a/src/components/App/ActionsToolbar/GraphViewControl/index.tsx b/src/components/App/ActionsToolbar/GraphViewControl/index.tsx index 6cbd79177..721520f5c 100644 --- a/src/components/App/ActionsToolbar/GraphViewControl/index.tsx +++ b/src/components/App/ActionsToolbar/GraphViewControl/index.tsx @@ -31,7 +31,7 @@ export const GraphViewControl = () => { } return ( - + {graphStyles.map((i) => ( changeGraphType(i)}> {IconsMapper[i]} @@ -44,7 +44,7 @@ export const GraphViewControl = () => { const Wrapper = styled(Flex).attrs({ direction: 'row', align: 'center', - justify: 'flex-start', + justify: 'space-between', })` padding: 6px 6px 6px 11px; background: ${colors.BG1}; diff --git a/src/components/App/MainToolbar/index.tsx b/src/components/App/MainToolbar/index.tsx index 49d2749ec..e48abf318 100644 --- a/src/components/App/MainToolbar/index.tsx +++ b/src/components/App/MainToolbar/index.tsx @@ -1,6 +1,7 @@ import styled from 'styled-components' import AddContentIcon from '~/components/Icons/AddContentIcon' import AddSourceIcon from '~/components/Icons/AddSourceIcon' +import SentimentDataIcon from '~/components/Icons/SentimentDataIcon' import SettingsIcon from '~/components/Icons/SettingsIcon' import SourcesTableIcon from '~/components/Icons/SourcesTableIcon' import { Flex } from '~/components/common/Flex' @@ -15,6 +16,7 @@ export const MainToolbar = () => { const { open: openSourcesModal } = useModal('sourcesTable') const { open: openContentAddModal } = useModal('addContent') const { open: openSourceAddModal } = useModal('addSource') + const { open: openSettingsModal } = useModal('settings') const handleOpenSidebar = (tab: SecondarySidebarActiveTab) => { setSecondarySidebarActiveTab(tab) @@ -22,7 +24,7 @@ export const MainToolbar = () => { return ( - handleOpenSidebar('about')}> + Second brain @@ -43,11 +45,17 @@ export const MainToolbar = () => { Source Table - + handleOpenSidebar('sentiment')}> + + + + Sentiment Data + + - Settings + Change Display ) @@ -108,7 +116,7 @@ const ActionButton = styled(Flex).attrs({ border-radius: 4px; background: #000; box-shadow: 0px 4px 8px 0px rgba(0, 0, 0, 0.25); - position absolute; + position: absolute; left: 90%; z-index: 99; white-space: nowrap; @@ -120,7 +128,7 @@ const ActionButton = styled(Flex).attrs({ } &:hover { - color: ${colors.white}; + color: ${colors.white}; &:before { width: 3px; diff --git a/src/components/App/index.tsx b/src/components/App/index.tsx index 1f039180e..8274939cd 100644 --- a/src/components/App/index.tsx +++ b/src/components/App/index.tsx @@ -23,6 +23,7 @@ import { E2ETests } from '~/utils/tests' import version from '~/utils/versionHelper' import { AddContentModal } from '../AddContentModal' import { AddSourceModal } from '../AddSourceModal' +import { SettingsModal } from '../SettingsModal' import { SourcesTableModal } from '../SourcesTableModal' import { Preloader } from '../Universe/Preloader' import { ActionsToolbar } from './ActionsToolbar' @@ -166,6 +167,7 @@ export const App = () => { + diff --git a/src/components/SettingsModal/Appearance/index.tsx b/src/components/SettingsModal/Appearance/index.tsx new file mode 100644 index 000000000..47e51d337 --- /dev/null +++ b/src/components/SettingsModal/Appearance/index.tsx @@ -0,0 +1,27 @@ +import styled from 'styled-components' +import { GraphViewControl } from '~/components/App/ActionsToolbar/GraphViewControl' +import { Button } from '~/components/Button' +import { Flex } from '~/components/common/Flex' +import { Text } from '~/components/common/Text' + +export const Appearance = () => ( + + Default graph view: + + + + + +) + +const Wrapper = styled(Flex)` + display: flex; + gap: 10px; + padding: 36px; +` + +const StyledText = styled(Text)` + font-family: Barlow; + font-size: 13px; + font-weight: 400; +` diff --git a/src/components/SettingsModal/General/index.tsx b/src/components/SettingsModal/General/index.tsx new file mode 100644 index 000000000..1b1b57b1b --- /dev/null +++ b/src/components/SettingsModal/General/index.tsx @@ -0,0 +1,112 @@ +import { FC } from 'react' +import { FormProvider, useForm } from 'react-hook-form' +import { ClipLoader } from 'react-spinners' +import styled from 'styled-components' +import { Button } from '~/components/Button' +import { Flex } from '~/components/common/Flex' +import { TextInput } from '~/components/common/TextInput' +import { requiredRule } from '~/constants' +import { TAboutParams, postAboutData } from '~/network/fetchSourcesData' +import { colors } from '~/utils/colors' + +type Props = { + initialValues: TAboutParams +} + +export const General: FC = ({ initialValues }) => { + const form = useForm({ defaultValues: initialValues, mode: 'onSubmit' }) + const { isSubmitting } = form.formState + + const onSubmit = form.handleSubmit(async (data) => { + try { + await postAboutData(data) + } catch (error) { + console.warn(error) + } + }) + + return ( + + + <> + + + + + + + + + + + + + + + + + {isSubmitting ? ( + + + + ) : ( + + )} + + + + + ) +} + +const SubmitLoader = styled(Flex).attrs({ + align: 'center', + background: 'primaryButton', + borderRadius: 8, + justify: 'center', +})` + padding: 16px 24px; + opacity: 0.5; +` + +const StyledForm = styled.form` + padding: 36px; +` diff --git a/src/components/SettingsModal/SourceView/index.tsx b/src/components/SettingsModal/SourceView/index.tsx new file mode 100644 index 000000000..5b74d40db --- /dev/null +++ b/src/components/SettingsModal/SourceView/index.tsx @@ -0,0 +1,210 @@ +/* eslint-disable react/no-unstable-nested-components */ +import Tab from '@mui/material/Tab' +import Tabs from '@mui/material/Tabs' +import * as React from 'react' +import { useEffect, useState } from 'react' +import styled from 'styled-components' +import { Button } from '~/components/Button' +import { Flex } from '~/components/common/Flex' +import { Text } from '~/components/common/Text' +import { TAboutParams, getAboutData } from '~/network/fetchSourcesData' +import { useUserStore } from '~/stores/useUserStore' +import { colors } from '~/utils/colors' +import { executeIfProd } from '~/utils/tests' +import { Appearance } from '../Appearance' +import { General } from '../General' + +interface TabPanelProps { + children?: React.ReactNode + index: number + value: number +} + +const admins = [ + '02c431e64078b10925584d64824c9d1d12eca05e2c56660ffa5ac84aa6946adfe5', + '03a9a8d953fe747d0dd94dd3c567ddc58451101e987e2d2bf7a4d1e10a2c89ff38', + '024efa31d1e4f98bccc415b222c9d971866013ad6f95f7d1ed9e8be8e3355a36ff', + '03bfe6723c06fb2b7546df1e8ca1a17ae5c504615da32c945425ccbe8d3ca6260d', + '024efa31d1e4f98bccc415b222c9d971866013ad6f95f7d1ed9e8be8e3355a36ff', +] + +const defaultData = { + description: '', + mission_statement: '', + search_term: '', + title: '', +} + +const TabPanel = (props: TabPanelProps) => { + const { children, value, index, ...other } = props + + return value === index ? ( + + ) : null +} + +function a11yProps(index: number) { + return { + id: `simple-tab-${index}`, + 'aria-controls': `simple-tabpanel-${index}`, + } +} + +export const SourcesView = () => { + const [value, setValue] = useState(0) + const [setIsAdmin, isAdmin, setPubKey, pubKey] = useUserStore((s) => [s.setIsAdmin, s.isAdmin, s.setPubKey, s.pubKey]) + const [loading, setLoading] = useState(false) + const [initialValues, setInitialValues] = useState(defaultData) + + useEffect(() => { + const init = async () => { + setLoading(true) + + try { + const response = await getAboutData() + + setInitialValues(response) + } catch (error) { + console.warn(error) + } finally { + setLoading(false) + } + } + + init() + }, []) + + const getSettingsLabel = () => (isAdmin ? 'Admin Settings' : 'Settings') + + const SettingsHeader = ({ children }: { children: React.ReactNode }) => ( + + + {getSettingsLabel()} + {resolveAdminActions()} + + {children} + + ) + + const authorize = async () => { + // skipping this for end to end test because it requires a sphinx-relay to be connected + await executeIfProd(async () => { + try { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + const enable = await sphinx.enable() + const pubKeyRes = enable?.pubkey + + setPubKey(pubKeyRes) + + if (pubKeyRes) { + setIsAdmin(pubKeyRes && admins.includes(pubKeyRes)) + } + } catch (error) { + console.warn(error) + } + }) + } + + const resolveAdminActions = () => { + if (!pubKey) { + return ( + + Admin + + ) + } + + if (pubKey && isAdmin) { + return null + } + + return You are not admin + } + + const handleChange = (event: React.SyntheticEvent, newValue: number) => { + setValue(newValue) + } + + return ( + + + + {isAdmin && } + + + + {isAdmin && ( + + {!loading ? : <>} + + )} + + + + + ) +} + +const StyledTabs = styled(Tabs)` + && { + .MuiTabs-indicator { + background: ${colors.primaryBlue}; + } + } +` + +const StyledHeader = styled(Flex)` + background: rgb(22, 24, 30); + padding: 40px 36px 0 0; +` + +const StyledTab = styled(Tab)` + && { + padding: 30px 0 24px; + color: ${colors.GRAY6}; + margin-left: 36px; + font-family: Barlow; + font-size: 16px; + font-style: normal; + font-weight: 500; + text-align: left; + + &.Mui-selected { + color: ${colors.white}; + } + } +` + +const TabPanelWrapper = styled(Flex)` + display: flex; + flex: 1; + min-height: 495px; + max-height: 495px; + height: fit-content; + min-width: 480px; +` + +const Wrapper = styled(Flex)` + min-height: 0; + flex: 1; +` + +const StyledText = styled(Text)` + font-size: 22px; + font-weight: 600; + font-family: Barlow; + padding: 0 0 0 36px; +` + +const EditButton = styled(Button)` + margin-left: auto; +` diff --git a/src/components/SettingsModal/index.tsx b/src/components/SettingsModal/index.tsx new file mode 100644 index 000000000..4e7720a04 --- /dev/null +++ b/src/components/SettingsModal/index.tsx @@ -0,0 +1,13 @@ +import { BaseModal } from '~/components/Modal' +import { useModal } from '~/stores/useModalStore' +import { SourcesView } from './SourceView' + +export const SettingsModal = () => { + const { close } = useModal('settings') + + return ( + + + + ) +} diff --git a/src/stores/useModalStore/index.ts b/src/stores/useModalStore/index.ts index a09cab01e..8464669c6 100644 --- a/src/stores/useModalStore/index.ts +++ b/src/stores/useModalStore/index.ts @@ -6,6 +6,7 @@ export type AvailableModals = | 'addContent' | 'editTopic' | 'addSource' + | 'settings' | 'mergeTopic' | 'briefDescription' @@ -23,6 +24,7 @@ const defaultData = { addContent: false, editTopic: false, addSource: false, + settings: false, mergeTopic: false, briefDescription: false, },