Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: create new namespaces and keys #139

Open
wants to merge 5 commits into
base: feat/two-page-layout
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 37 additions & 16 deletions i18n/en.pot
Original file line number Diff line number Diff line change
Expand Up @@ -5,27 +5,39 @@ msgstr ""
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
"POT-Creation-Date: 2024-12-18T09:41:03.249Z\n"
"PO-Revision-Date: 2024-12-18T09:41:03.250Z\n"
"POT-Creation-Date: 2024-12-19T16:36:13.574Z\n"
"PO-Revision-Date: 2024-12-19T16:36:13.575Z\n"

msgid "An error has occurred"
msgstr "An error has occurred"

msgid "404 Page Not Found"
msgstr "404 Page Not Found"

msgid "Back to datastore"
msgstr "Back to datastore"

msgid "Back to all namespaces"
msgstr "Back to all namespaces"
msgid "An error has occurred. Try again"
msgstr "An error has occurred. Try again"

msgid "Search namespaces"
msgstr "Search namespaces"
msgid "Cancel"
msgstr "Cancel"

msgid "New Namespace"
msgstr "New Namespace"
msgid "Add"
msgstr "Add"

msgid "Key"
msgstr "Key"

msgid "Namespace"
msgstr "Namespace"

msgid "Back to all namespaces"
msgstr "Back to all namespaces"

msgid "Configure Namespaces"
msgstr "Configure Namespaces"

msgid "Close"
msgstr "Close"

Expand All @@ -38,23 +50,32 @@ msgstr "Save changes"
msgid "Choose a key to start editing"
msgstr "Choose a key to start editing"

msgid "Keys"
msgstr "Keys"
msgid "DataStore"
msgstr "DataStore"

msgid "UserDataStore"
msgstr "UserDataStore"

msgid "keys"
msgstr "keys"

msgid "New Key"
msgstr "New Key"

msgid "Search keys"
msgstr "Search keys"

msgid "Key"
msgstr "Key"
msgid "Add New Key"
msgstr "Add New Key"

msgid "DataStore"
msgstr "DataStore"
msgid "Search namespaces"
msgstr "Search namespaces"

msgid "UserDataStore"
msgstr "UserDataStore"
msgid "New Namespace"
msgstr "New Namespace"

msgid "Add New Namespace"
msgstr "Add New Namespace"

msgid "Search"
msgstr "Search"
Expand Down
61 changes: 61 additions & 0 deletions src/components/modals/CreateModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import {
Modal,
ModalTitle,
ModalContent,
ModalActions,
Button,
ButtonStrip,
ReactFinalForm,
} from '@dhis2/ui'
import React from 'react'
import i18n from '../../locales'

const { Form } = ReactFinalForm

interface FieldValues {
key: string
namespace?: string
}

interface CreateModalProps {
closeModal: () => void
handleCreate: ({ key, namespace }: FieldValues) => void
children: React.ReactNode
title: string
}

const CreateModal = ({
handleCreate,
closeModal,
children,
title,
}: CreateModalProps) => {
const onSubmit = (values) => {
handleCreate(values)
}

return (
<Modal position="middle">
<Form onSubmit={onSubmit}>
{({ handleSubmit }) => (
<form onSubmit={handleSubmit}>
<ModalTitle>{title}</ModalTitle>
<ModalContent>{children}</ModalContent>
<ModalActions>
<ButtonStrip end>
<Button secondary onClick={closeModal}>
{i18n.t('Cancel')}
</Button>
<Button primary type="submit">
{i18n.t('Add')}
</Button>
</ButtonStrip>
</ModalActions>
</form>
)}
</Form>
</Modal>
)
}

export default CreateModal
41 changes: 41 additions & 0 deletions src/components/modals/Fields.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import {
ReactFinalForm,
InputFieldFF,
hasValue,
composeValidators,
alphaNumeric,
} from '@dhis2/ui'
import React from 'react'
import i18n from '../../locales'

const { Field } = ReactFinalForm

export const KeysField = ({ initialFocus }: { initialFocus?: boolean }) => {
return (
<Field
name="key"
component={InputFieldFF}
required
label={i18n.t('Key')}
validate={composeValidators(hasValue, alphaNumeric)}
initialFocus={initialFocus}
/>
)
}

export const NamespaceField = ({
initialFocus,
}: {
initialFocus?: boolean
}) => {
return (
<Field
name="namespace"
component={InputFieldFF}
required
label={i18n.t('Namespace')}
validate={composeValidators(hasValue, alphaNumeric)}
initialFocus={initialFocus}
/>
)
}
5 changes: 0 additions & 5 deletions src/components/pages/Namespaces.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,6 @@ const dataStoreNamespacesQuery = {
},
}

export type FieldValues = {
namespace?: string
key?: string
}

const NamespacesPage = () => {
const navigate = useNavigate()
const { store } = useParams()
Expand Down
57 changes: 53 additions & 4 deletions src/components/sections/KeysDataSection.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import { useDataQuery } from '@dhis2/app-runtime'
import { useDataEngine, useDataQuery } from '@dhis2/app-runtime'
import { IconAdd16, colors } from '@dhis2/ui'
import React, { useEffect } from 'react'
import React, { useEffect, useState } from 'react'
import { useParams } from 'react-router-dom'
import classes from '../../App.module.css'
import useCustomAlert from '../../hooks/useCustomAlert'
import i18n from '../../locales'
import ErrorNotice from '../error/ErrorNotice'
import PanelHeader from '../header/PanelHeader'
import CenteredLoader from '../loader/Loader'
import CreateModal from '../modals/CreateModal'
import { KeysField } from '../modals/Fields'
import ItemsTable from '../table/ItemsTable'
import CreateButton from './CreateButton'
import SearchField from './SearchField'
Expand All @@ -16,7 +19,12 @@ interface QueryResults {
}

const KeysDataSection = ({ query }) => {
const { namespace: currentNamespace } = useParams()
const engine = useDataEngine()
const { store, namespace: currentNamespace } = useParams()

const [openCreateModal, setOpenCreateModal] = useState(false)

const { showError, showSuccess } = useCustomAlert()

const { error, loading, data, refetch } = useDataQuery<QueryResults>(
query,
Expand All @@ -27,6 +35,38 @@ const KeysDataSection = ({ query }) => {
}
)

const handleCreate = async ({ key }) => {
await engine.mutate(
{
type: 'create',
resource: `${store}/${currentNamespace}/${key}`,
data: () => ({}),
},
{
onComplete() {
setOpenCreateModal(false)
showSuccess(
i18n.t("Key '{{key}}' added successfully", {
key,
})
)
refetch({ id: currentNamespace })
},
onError(error) {
showError(
i18n.t(
'There was a problem adding this key - {{error}}',
{
error: error.message,
interpolation: { escapeValue: false },
}
)
)
},
}
)
}

useEffect(() => {
refetch({ id: currentNamespace })
}, [currentNamespace])
Expand All @@ -47,7 +87,7 @@ const KeysDataSection = ({ query }) => {
</span>
<CreateButton
label={i18n.t('New Key')}
handleClick={() => console.log('create new key')}
handleClick={() => setOpenCreateModal(true)}
icon={<IconAdd16 color={colors.grey600} />}
/>
</PanelHeader>
Expand All @@ -57,6 +97,15 @@ const KeysDataSection = ({ query }) => {
<div>
{data && <ItemsTable data={data} label={i18n.t('Key')} />}
</div>
{openCreateModal && (
<CreateModal
title={i18n.t('Add New Key')}
closeModal={() => setOpenCreateModal(false)}
handleCreate={handleCreate}
>
<KeysField initialFocus />
</CreateModal>
)}
</>
)
}
Expand Down
66 changes: 62 additions & 4 deletions src/components/sections/NamespaceDataSection.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
import { useDataQuery } from '@dhis2/app-runtime'
import { useDataEngine, useDataQuery } from '@dhis2/app-runtime'
import { IconAdd24, colors } from '@dhis2/ui'
import React from 'react'
import React, { useState } from 'react'
import { useNavigate, useParams } from 'react-router-dom'
import classes from '../../App.module.css'
import useCustomAlert from '../../hooks/useCustomAlert'
import i18n from '../../locales'
import ErrorNotice from '../error/ErrorNotice'
import CenteredLoader from '../loader/Loader'
import CreateModal from '../modals/CreateModal'
import { KeysField, NamespaceField } from '../modals/Fields'
import ItemsTable from '../table/ItemsTable'
import CreateButton from './CreateButton'
import SearchField from './SearchField'
Expand All @@ -14,7 +18,51 @@ interface QueryResults {
}

const NamespaceDataSection = ({ query }) => {
const { error, loading, data } = useDataQuery<QueryResults>(query)
const engine = useDataEngine()
const navigate = useNavigate()
const { store } = useParams()
const [openCreateModal, setOpenCreateModal] = useState(false)

const { showError, showSuccess } = useCustomAlert()

const { error, loading, data, refetch } = useDataQuery<QueryResults>(query)

const handleCreate = async (values) => {
await engine.mutate(
{
type: 'create',
resource: `${store}/${values?.namespace}/${values?.key}`,
data: () => ({}),
},
{
onComplete() {
showSuccess(
i18n.t(
"Namespace '{{namespace}}' and key '{{key}}' added successfully!",
{
namespace: values.namespace,
key: values.key,
}
)
)
refetch()
navigate(`edit/${values?.namespace}`)
setOpenCreateModal(false)
},
onError(error) {
showError(
i18n.t(
'There was a problem adding this namespace - {{error}}',
{
error: error.message,
interpolation: { escapeValue: false },
}
)
)
},
}
)
}

if (error) {
return <ErrorNotice />
Expand All @@ -30,13 +78,23 @@ const NamespaceDataSection = ({ query }) => {
<SearchField placeholder={i18n.t('Search namespaces')} />
<CreateButton
label={i18n.t('New Namespace')}
handleClick={() => console.log('create new namespace')}
handleClick={() => setOpenCreateModal(true)}
icon={<IconAdd24 color={colors.grey600} />}
/>
</div>
<div>
{data && <ItemsTable data={data} label={i18n.t('Namespace')} />}
</div>
{openCreateModal && (
<CreateModal
title={i18n.t('Add New Namespace')}
closeModal={() => setOpenCreateModal(false)}
handleCreate={handleCreate}
>
<NamespaceField initialFocus />
<KeysField />
</CreateModal>
)}
</>
)
}
Expand Down
Loading
Loading