Skip to content

Commit ae7dc49

Browse files
committed
feat: allow selection of db type on frontend
1 parent 2655ab3 commit ae7dc49

File tree

9 files changed

+92
-41
lines changed

9 files changed

+92
-41
lines changed

packages/backend/src/graphql/custom-resolvers/table-metadata.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ import type { Resolvers } from '../__generated__/types.generated'
44

55
type TableMetadataResolver = Resolvers['TableMetadata']
66

7+
const databaseType: TableMetadataResolver['databaseType'] = async (parent) => {
8+
return parent.db
9+
}
10+
711
const columns: TableMetadataResolver['columns'] = async (parent) => {
812
const columns = await parent
913
.$relatedQuery('columns')
@@ -33,4 +37,5 @@ const collaborators: TableMetadataResolver['collaborators'] = async (
3337
export default {
3438
columns,
3539
collaborators,
40+
databaseType,
3641
} satisfies TableMetadataResolver

packages/backend/src/graphql/schema.graphql

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -807,6 +807,7 @@ input DeleteTableCollaboratorInput {
807807

808808
type TableMetadata {
809809
id: ID!
810+
databaseType: DatabaseType!
810811
name: String!
811812
columns: [TableColumnMetadata!]
812813
lastAccessedAt: String!

packages/frontend/src/components/ControlledAutocomplete/AddNewOptionModal.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
} from '@chakra-ui/react'
1414
import { Button, FormLabel, Input } from '@opengovsg/design-system-react'
1515

16+
import { DatabaseType } from '@/graphql/__generated__/graphql'
1617
import client from '@/graphql/client'
1718
import { CREATE_TABLE } from '@/graphql/mutations/tiles/create-table'
1819
import { UPDATE_TABLE } from '@/graphql/mutations/tiles/update-table'
@@ -49,6 +50,7 @@ export function useCreateNewOption(setValue: (newValue: string) => void) {
4950
input: {
5051
name: inputValue.trim(),
5152
isBlank: true,
53+
databaseType: DatabaseType.Pg,
5254
},
5355
},
5456
})

packages/frontend/src/graphql/queries/tiles/get-table.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ export const GET_TABLE = gql`
66
id
77
name
88
viewOnlyKey
9+
databaseType
910
columns {
1011
id
1112
name

packages/frontend/src/pages/Tile/components/TableBanner/ImportCsvButton.tsx

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import Papa, { ParseMeta, ParseResult } from 'papaparse'
2929
import { SetRequired } from 'type-fest'
3030

3131
import PrimarySpinner from '@/components/PrimarySpinner'
32+
import { DatabaseType } from '@/graphql/__generated__/graphql'
3233
import { CREATE_ROWS } from '@/graphql/mutations/tiles/create-rows'
3334
import { GET_TABLE } from '@/graphql/queries/tiles/get-table'
3435

@@ -49,7 +50,10 @@ type IMPORT_STATUS =
4950
// 2 MB in bytes
5051
const MAX_FILE_SIZE = 2 * 1000 * 1000
5152
// Add row chunk size
52-
const CHUNK_SIZE = 100
53+
const CHUNK_SIZE = {
54+
[DatabaseType.Pg]: 1000,
55+
[DatabaseType.Ddb]: 100,
56+
}
5357

5458
const ImportStatus = ({
5559
columnsToCreate,
@@ -134,7 +138,7 @@ export const ImportCsvModalContent = ({
134138
onPostImport?: () => void
135139
onBack?: () => void
136140
}) => {
137-
const { tableId, tableColumns, refetch } = useTableContext()
141+
const { tableId, tableColumns, refetch, databaseType } = useTableContext()
138142
const { createColumns } = useUpdateTable()
139143
const [createRows] = useMutation(CREATE_ROWS)
140144
const [getTableData] = useLazyQuery<{
@@ -255,7 +259,7 @@ export const ImportCsvModalContent = ({
255259
setImportStatus('importing')
256260
setRowsToImport(mappedData.length)
257261
setRowsImported(0)
258-
const chunkedData = chunk(mappedData, CHUNK_SIZE)
262+
const chunkedData = chunk(mappedData, CHUNK_SIZE[databaseType])
259263

260264
for (let i = 0; i < chunkedData.length; i++) {
261265
await createRows({
@@ -269,7 +273,7 @@ export const ImportCsvModalContent = ({
269273
if (i === chunkedData.length - 1 && !onPreImport) {
270274
await refetch()
271275
}
272-
setRowsImported((i + 1) * CHUNK_SIZE)
276+
setRowsImported((i + 1) * CHUNK_SIZE[databaseType])
273277
}
274278
}
275279
setImportStatus('completed')
@@ -291,6 +295,7 @@ export const ImportCsvModalContent = ({
291295
onPreImport,
292296
refetch,
293297
result,
298+
databaseType,
294299
tableColumns,
295300
tableId,
296301
])

packages/frontend/src/pages/Tile/contexts/TableContext.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,15 @@ import React, {
1313
useState,
1414
} from 'react'
1515

16+
import { DatabaseType } from '@/graphql/__generated__/graphql'
17+
1618
import { flattenRows } from '../helpers/flatten-rows'
1719
import { EditMode, GenericRowData } from '../types'
1820

1921
interface TableContextProps {
2022
tableId: string
2123
tableName: string
24+
databaseType: DatabaseType
2225
flattenedData: GenericRowData[]
2326
tableColumns: ITableColumnMetadata[]
2427
filteredDataRef: MutableRefObject<GenericRowData[]>
@@ -49,6 +52,7 @@ export const useTableContext = () => {
4952
interface TableContextProviderProps {
5053
tableId: string
5154
tableName: string
55+
databaseType: DatabaseType
5256
tableColumns: ITableColumnMetadata[]
5357
tableRows: ITableRow[]
5458
children: React.ReactNode
@@ -64,6 +68,7 @@ interface TableContextProviderProps {
6468
export const TableContextProvider = ({
6569
tableId,
6670
tableName,
71+
databaseType,
6772
tableColumns,
6873
tableRows,
6974
children,
@@ -86,6 +91,7 @@ export const TableContextProvider = ({
8691
value={{
8792
tableId,
8893
tableName,
94+
databaseType,
8995
flattenedData,
9096
tableColumns,
9197
allDataRef,

packages/frontend/src/pages/Tile/index.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,13 +80,14 @@ export default function Tile(): JSX.Element | null {
8080
return null
8181
}
8282

83-
const { id, name, columns, viewOnlyKey, collaborators } =
83+
const { id, name, columns, viewOnlyKey, collaborators, databaseType } =
8484
getTableData.getTable
8585

8686
return (
8787
<TableContextProvider
8888
tableName={name}
8989
tableId={id}
90+
databaseType={databaseType}
9091
tableColumns={columns}
9192
tableRows={rows}
9293
viewOnlyKey={viewOnlyKey}

packages/frontend/src/pages/Tiles/components/CreateTileButton.tsx

Lines changed: 65 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { useNavigate } from 'react-router-dom'
66
import { useMutation } from '@apollo/client'
77
import {
88
Flex,
9+
FormControl,
910
Modal,
1011
ModalBody,
1112
ModalContent,
@@ -16,14 +17,17 @@ import {
1617
useDisclosure,
1718
} from '@chakra-ui/react'
1819
import {
20+
Badge,
1921
Button,
2022
FormLabel,
2123
Input,
2224
ModalCloseButton,
2325
Tile,
2426
} from '@opengovsg/design-system-react'
2527

28+
import { SingleSelect } from '@/components/SingleSelect'
2629
import * as URLS from '@/config/urls'
30+
import { DatabaseType } from '@/graphql/__generated__/graphql'
2731
import { CREATE_TABLE } from '@/graphql/mutations/tiles/create-table'
2832
import { ImportCsvModalContent } from '@/pages/Tile/components/TableBanner/ImportCsvButton'
2933
import { TableContextProvider } from '@/pages/Tile/contexts/TableContext'
@@ -33,12 +37,16 @@ type TILE_CREATE_MODE = 'import' | 'new'
3337
const CreateTileForm = ({
3438
tableName,
3539
setTableName,
40+
databaseType,
41+
setDatabaseType,
3642
onSubmit,
3743
isSubmitting,
3844
onImportNext,
3945
}: {
4046
tableName: string
4147
setTableName: (tableName: string) => void
48+
databaseType: DatabaseType
49+
setDatabaseType: (databaseType: DatabaseType) => void
4250
onSubmit: () => Promise<void>
4351
isSubmitting: boolean
4452
onImportNext: () => void
@@ -49,41 +57,55 @@ const CreateTileForm = ({
4957
<>
5058
<ModalHeader>Create a Tile</ModalHeader>
5159
<ModalCloseButton />
52-
<ModalBody>
53-
<FormLabel isRequired>Enter a name for your new tile</FormLabel>
54-
<Input
55-
value={tableName}
56-
onChange={(e) => setTableName(e.target.value)}
57-
/>
58-
<FormLabel mt={8} isRequired>
59-
How do you want to start?
60-
</FormLabel>
61-
<Flex gap={4}>
62-
<Tile
63-
icon={BiSpreadsheet}
64-
flex={1}
65-
onClick={() => setCreateMode('import')}
66-
isSelected={createMode === 'import'}
67-
>
68-
<Text textStyle="h5" mt={4}>
69-
Import CSV
70-
</Text>
71-
<Text textStyle="body-2">
72-
Start with data that you have already collected
73-
</Text>
74-
</Tile>
75-
<Tile
76-
icon={BiTable}
77-
flex={1}
78-
onClick={() => setCreateMode('new')}
79-
isSelected={createMode === 'new'}
80-
>
81-
<Text textStyle="h5" mt={4}>
82-
Start from scratch
83-
</Text>
84-
<Text textStyle="body-2">Start with a blank database</Text>
85-
</Tile>
86-
</Flex>
60+
<ModalBody gap={8} display="flex" flexDirection="column">
61+
<FormControl>
62+
<FormLabel isRequired>Enter a name for your new tile</FormLabel>
63+
<Input
64+
value={tableName}
65+
onChange={(e) => setTableName(e.target.value)}
66+
/>
67+
</FormControl>
68+
<FormControl>
69+
<FormLabel>Select version</FormLabel>
70+
<SingleSelect
71+
items={[
72+
{
73+
label: 'Tiles v2 (PostgreSQL)',
74+
value: DatabaseType.Pg,
75+
badge: <Badge>NEW</Badge>,
76+
},
77+
{ label: 'Tiles v1 (DynamoDB)', value: DatabaseType.Ddb },
78+
]}
79+
value={databaseType}
80+
onChange={(value) => setDatabaseType(value as DatabaseType)}
81+
name="databaseType"
82+
/>
83+
</FormControl>
84+
<FormControl>
85+
<FormLabel isRequired>How do you want to start?</FormLabel>
86+
<Flex gap={4} direction={{ base: 'column', sm: 'row' }}>
87+
<Tile
88+
icon={BiSpreadsheet}
89+
flex={1}
90+
onClick={() => setCreateMode('import')}
91+
isSelected={createMode === 'import'}
92+
>
93+
<Text textStyle="h5">Import CSV</Text>
94+
<Text textStyle="body-2">
95+
Start with data that you have already collected
96+
</Text>
97+
</Tile>
98+
<Tile
99+
icon={BiTable}
100+
flex={1}
101+
onClick={() => setCreateMode('new')}
102+
isSelected={createMode === 'new'}
103+
>
104+
<Text textStyle="h5">Start from scratch</Text>
105+
<Text textStyle="body-2">Start with a blank database</Text>
106+
</Tile>
107+
</Flex>
108+
</FormControl>
87109
</ModalBody>
88110
<ModalFooter>
89111
<Button
@@ -104,6 +126,9 @@ const CreateTileModal = ({ onClose }: { onClose: () => void }): JSX.Element => {
104126
const [tableName, setTableName] = useState('')
105127
const [showImportModal, setShowImportModal] = useState(false)
106128
const [tableData, setTableData] = useState<ITableMetadata | null>(null)
129+
const [databaseType, setDatabaseType] = useState<DatabaseType>(
130+
DatabaseType.Pg,
131+
)
107132

108133
const [createTableMutation, { loading }] = useMutation<{
109134
createTable: ITableMetadata
@@ -116,6 +141,7 @@ const CreateTileModal = ({ onClose }: { onClose: () => void }): JSX.Element => {
116141
input: {
117142
name: tableName,
118143
isBlank,
144+
databaseType,
119145
},
120146
},
121147
})
@@ -125,7 +151,7 @@ const CreateTileModal = ({ onClose }: { onClose: () => void }): JSX.Element => {
125151
setTableData(data.createTable)
126152
return data.createTable
127153
},
128-
[createTableMutation, tableName],
154+
[createTableMutation, databaseType, tableName],
129155
)
130156

131157
const navigateToTile = useCallback(
@@ -155,6 +181,7 @@ const CreateTileModal = ({ onClose }: { onClose: () => void }): JSX.Element => {
155181
<ModalContent>
156182
{showImportModal ? (
157183
<TableContextProvider
184+
databaseType={databaseType}
158185
tableName={tableName}
159186
tableId={tableData ? tableData.id : ''}
160187
tableColumns={[]}
@@ -174,6 +201,8 @@ const CreateTileModal = ({ onClose }: { onClose: () => void }): JSX.Element => {
174201
<CreateTileForm
175202
tableName={tableName}
176203
setTableName={setTableName}
204+
setDatabaseType={setDatabaseType}
205+
databaseType={databaseType}
177206
onSubmit={onSubmit}
178207
isSubmitting={loading}
179208
onImportNext={() => setShowImportModal(true)}

packages/types/index.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -903,6 +903,7 @@ export interface ITableColumnMetadata {
903903
export interface ITableMetadata {
904904
id: string
905905
name: string
906+
databaseType: DatabaseType
906907
columns: ITableColumnMetadata[]
907908
lastAccessedAt: string
908909
viewOnlyKey?: string

0 commit comments

Comments
 (0)