Skip to content

Commit 708c15e

Browse files
committed
Make database rows use context better
1 parent 0ebf842 commit 708c15e

File tree

5 files changed

+83
-86
lines changed

5 files changed

+83
-86
lines changed

resources/scripts/components/server/databases/CreateDatabaseButton.tsx

Lines changed: 15 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,14 @@
11
import React, { useState } from 'react';
2-
import { ServerDatabase } from '@/api/server/getServerDatabases';
32
import Modal from '@/components/elements/Modal';
43
import { Form, Formik, FormikHelpers } from 'formik';
54
import Field from '@/components/elements/Field';
65
import { object, string } from 'yup';
76
import createServerDatabase from '@/api/server/createServerDatabase';
87
import { ServerContext } from '@/state/server';
9-
import { Actions, useStoreActions } from 'easy-peasy';
10-
import { ApplicationStore } from '@/state';
118
import { httpErrorToHuman } from '@/api/http';
129
import FlashMessageRender from '@/components/FlashMessageRender';
10+
import useFlash from '@/plugins/useFlash';
11+
import useServer from '@/plugins/useServer';
1312

1413
interface Values {
1514
databaseName: string;
@@ -27,28 +26,25 @@ const schema = object().shape({
2726
.matches(/^([1-9]{1,3}|%)(\.([0-9]{1,3}|%))?(\.([0-9]{1,3}|%))?(\.([0-9]{1,3}|%))?$/, 'A valid connection address must be provided.'),
2827
});
2928

30-
export default ({ onCreated }: { onCreated: (database: ServerDatabase) => void }) => {
29+
export default () => {
30+
const { uuid } = useServer();
31+
const { addError, clearFlashes } = useFlash();
3132
const [ visible, setVisible ] = useState(false);
32-
const { addFlash, clearFlashes } = useStoreActions((actions: Actions<ApplicationStore>) => actions.flashes);
33-
const server = ServerContext.useStoreState(state => state.server.data!);
33+
34+
const appendDatabase = ServerContext.useStoreActions(actions => actions.databases.appendDatabase);
3435

3536
const submit = (values: Values, { setSubmitting }: FormikHelpers<Values>) => {
36-
clearFlashes();
37-
createServerDatabase(server.uuid, { ...values })
37+
clearFlashes('database:create');
38+
createServerDatabase(uuid, { ...values })
3839
.then(database => {
39-
onCreated(database);
40+
appendDatabase(database);
4041
setVisible(false);
4142
})
4243
.catch(error => {
4344
console.log(error);
44-
addFlash({
45-
key: 'create-database-modal',
46-
type: 'error',
47-
title: 'Error',
48-
message: httpErrorToHuman(error),
49-
});
50-
})
51-
.then(() => setSubmitting(false));
45+
addError({ key: 'database:create', message: httpErrorToHuman(error) });
46+
setSubmitting(false);
47+
});
5248
};
5349

5450
return (
@@ -69,7 +65,7 @@ export default ({ onCreated }: { onCreated: (database: ServerDatabase) => void }
6965
setVisible(false);
7066
}}
7167
>
72-
<FlashMessageRender byKey={'create-database-modal'} className={'mb-6'}/>
68+
<FlashMessageRender byKey={'database:create'} className={'mb-6'}/>
7369
<h3 className={'mb-6'}>Create new database</h3>
7470
<Form className={'m-0'}>
7571
<Field
@@ -105,7 +101,7 @@ export default ({ onCreated }: { onCreated: (database: ServerDatabase) => void }
105101
)
106102
}
107103
</Formik>
108-
<button className={'btn btn-primary btn-lg'} onClick={() => setVisible(true)}>
104+
<button className={'btn btn-primary btn-sm'} onClick={() => setVisible(true)}>
109105
New Database
110106
</button>
111107
</React.Fragment>

resources/scripts/components/server/databases/DatabaseRow.tsx

Lines changed: 13 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -9,31 +9,28 @@ import { Form, Formik, FormikHelpers } from 'formik';
99
import Field from '@/components/elements/Field';
1010
import { object, string } from 'yup';
1111
import FlashMessageRender from '@/components/FlashMessageRender';
12-
import { Actions, useStoreActions } from 'easy-peasy';
13-
import { ApplicationStore } from '@/state';
1412
import { ServerContext } from '@/state/server';
1513
import deleteServerDatabase from '@/api/server/deleteServerDatabase';
1614
import { httpErrorToHuman } from '@/api/http';
1715
import RotatePasswordButton from '@/components/server/databases/RotatePasswordButton';
1816
import Can from '@/components/elements/Can';
17+
import { ServerDatabase } from '@/api/server/getServerDatabases';
18+
import useServer from '@/plugins/useServer';
19+
import useFlash from '@/plugins/useFlash';
1920

2021
interface Props {
21-
databaseId: string | number;
22+
database: ServerDatabase;
2223
className?: string;
23-
onDelete: () => void;
2424
}
2525

26-
export default ({ databaseId, className, onDelete }: Props) => {
26+
export default ({ database, className }: Props) => {
27+
const { uuid } = useServer();
28+
const { addError, clearFlashes } = useFlash();
2729
const [ visible, setVisible ] = useState(false);
28-
const database = ServerContext.useStoreState(state => state.databases.items.find(item => item.id === databaseId));
29-
const appendDatabase = ServerContext.useStoreActions(actions => actions.databases.appendDatabase);
3030
const [ connectionVisible, setConnectionVisible ] = useState(false);
31-
const { addFlash, clearFlashes } = useStoreActions((actions: Actions<ApplicationStore>) => actions.flashes);
32-
const server = ServerContext.useStoreState(state => state.server.data!);
3331

34-
if (!database) {
35-
return null;
36-
}
32+
const appendDatabase = ServerContext.useStoreActions(actions => actions.databases.appendDatabase);
33+
const removeDatabase = ServerContext.useStoreActions(actions => actions.databases.removeDatabase);
3734

3835
const schema = object().shape({
3936
confirm: string()
@@ -43,20 +40,15 @@ export default ({ databaseId, className, onDelete }: Props) => {
4340

4441
const submit = (values: { confirm: string }, { setSubmitting }: FormikHelpers<{ confirm: string }>) => {
4542
clearFlashes();
46-
deleteServerDatabase(server.uuid, database.id)
43+
deleteServerDatabase(uuid, database.id)
4744
.then(() => {
4845
setVisible(false);
49-
setTimeout(() => onDelete(), 150);
46+
setTimeout(() => removeDatabase(database.id), 150);
5047
})
5148
.catch(error => {
5249
console.error(error);
5350
setSubmitting(false);
54-
addFlash({
55-
key: 'delete-database-modal',
56-
type: 'error',
57-
title: 'Error',
58-
message: httpErrorToHuman(error),
59-
});
51+
addError({ key: 'database:delete', message: httpErrorToHuman(error) });
6052
});
6153
};
6254

@@ -78,7 +70,7 @@ export default ({ databaseId, className, onDelete }: Props) => {
7870
resetForm();
7971
}}
8072
>
81-
<FlashMessageRender byKey={'delete-database-modal'} className={'mb-6'}/>
73+
<FlashMessageRender byKey={'database:delete'} className={'mb-6'}/>
8274
<h3 className={'mb-6'}>Confirm database deletion</h3>
8375
<p className={'text-sm'}>
8476
Deleting a database is a permanent action, it cannot be undone. This will permanetly

resources/scripts/components/server/databases/DatabasesContainer.tsx

Lines changed: 21 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,70 +1,68 @@
11
import React, { useEffect, useState } from 'react';
22
import getServerDatabases from '@/api/server/getServerDatabases';
33
import { ServerContext } from '@/state/server';
4-
import { Actions, useStoreActions } from 'easy-peasy';
5-
import { ApplicationStore } from '@/state';
64
import { httpErrorToHuman } from '@/api/http';
75
import FlashMessageRender from '@/components/FlashMessageRender';
86
import DatabaseRow from '@/components/server/databases/DatabaseRow';
97
import Spinner from '@/components/elements/Spinner';
108
import { CSSTransition } from 'react-transition-group';
119
import CreateDatabaseButton from '@/components/server/databases/CreateDatabaseButton';
1210
import Can from '@/components/elements/Can';
11+
import useFlash from '@/plugins/useFlash';
12+
import useServer from '@/plugins/useServer';
13+
import ListRefreshIndicator from '@/components/elements/ListRefreshIndicator';
1314

1415
export default () => {
16+
const { uuid, featureLimits } = useServer();
17+
const { addError, clearFlashes } = useFlash();
1518
const [ loading, setLoading ] = useState(true);
16-
const server = ServerContext.useStoreState(state => state.server.data!);
17-
const databases = ServerContext.useStoreState(state => state.databases.items);
18-
const { setDatabases, appendDatabase, removeDatabase } = ServerContext.useStoreActions(state => state.databases);
19-
const { addFlash, clearFlashes } = useStoreActions((actions: Actions<ApplicationStore>) => actions.flashes);
19+
20+
const databases = ServerContext.useStoreState(state => state.databases.data);
21+
const setDatabases = ServerContext.useStoreActions(state => state.databases.setDatabases);
2022

2123
useEffect(() => {
2224
setLoading(!databases.length);
2325
clearFlashes('databases');
2426

25-
getServerDatabases(server.uuid)
26-
.then(databases => {
27-
setDatabases(databases);
28-
setLoading(false);
27+
getServerDatabases(uuid)
28+
.then(databases => setDatabases(databases))
29+
.catch(error => {
30+
console.error(error);
31+
addError({ key: 'databases', message: httpErrorToHuman(error) });
2932
})
30-
.catch(error => addFlash({
31-
key: 'databases',
32-
title: 'Error',
33-
message: httpErrorToHuman(error),
34-
type: 'error',
35-
}));
33+
.then(() => setLoading(false));
3634
}, []);
3735

3836
return (
3937
<div className={'my-10 mb-6'}>
40-
<FlashMessageRender byKey={'databases'}/>
41-
{loading ?
38+
<FlashMessageRender byKey={'databases'} className={'mb-4'}/>
39+
{(!databases.length && loading) ?
4240
<Spinner size={'large'} centered={true}/>
4341
:
4442
<CSSTransition classNames={'fade'} timeout={250}>
4543
<>
44+
<ListRefreshIndicator visible={loading}/>
4645
{databases.length > 0 ?
4746
databases.map((database, index) => (
4847
<DatabaseRow
4948
key={database.id}
50-
databaseId={database.id}
51-
onDelete={() => removeDatabase(database)}
49+
database={database}
5250
className={index > 0 ? 'mt-1' : undefined}
5351
/>
5452
))
5553
:
5654
<p className={'text-center text-sm text-neutral-400'}>
57-
{server.featureLimits.databases > 0 ?
55+
{featureLimits.databases > 0 ?
5856
`It looks like you have no databases.`
5957
:
6058
`Databases cannot be created for this server.`
6159
}
6260
</p>
6361
}
6462
<Can action={'database.create'}>
65-
{server.featureLimits.databases > 0 &&
63+
{featureLimits.databases > 0 &&
6664
<div className={'mt-6 flex justify-end'}>
67-
<CreateDatabaseButton onCreated={appendDatabase}/>
65+
<CreateDatabaseButton/>
6866
</div>
6967
}
7068
</Can>
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { action, Action } from 'easy-peasy';
2+
import { ServerDatabase } from '@/api/server/getServerDatabases';
3+
4+
export interface ServerDatabaseStore {
5+
data: ServerDatabase[];
6+
setDatabases: Action<ServerDatabaseStore, ServerDatabase[]>;
7+
appendDatabase: Action<ServerDatabaseStore, ServerDatabase>;
8+
removeDatabase: Action<ServerDatabaseStore, string>;
9+
}
10+
11+
const databases: ServerDatabaseStore = {
12+
data: [],
13+
14+
setDatabases: action((state, payload) => {
15+
state.data = payload;
16+
}),
17+
18+
appendDatabase: action((state, payload) => {
19+
if (state.data.find(database => database.id === payload.id)) {
20+
state.data = state.data.map(database => database.id === payload.id ? payload : database);
21+
} else {
22+
state.data = [ ...state.data, payload ];
23+
}
24+
}),
25+
26+
removeDatabase: action((state, payload) => {
27+
state.data = [ ...state.data.filter(database => database.id !== payload) ];
28+
}),
29+
};
30+
31+
export default databases;

resources/scripts/state/server/index.ts

Lines changed: 3 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import getServer, { Server } from '@/api/server/getServer';
22
import { action, Action, createContextStore, thunk, Thunk } from 'easy-peasy';
33
import socket, { SocketStore } from './socket';
4-
import { ServerDatabase } from '@/api/server/getServerDatabases';
54
import files, { ServerFileStore } from '@/state/server/files';
65
import subusers, { ServerSubuserStore } from '@/state/server/subusers';
76
import { composeWithDevTools } from 'redux-devtools-extension';
87
import backups, { ServerBackupStore } from '@/state/server/backups';
98
import schedules, { ServerScheduleStore } from '@/state/server/schedules';
9+
import databases, { ServerDatabaseStore } from '@/state/server/databases';
1010

1111
export type ServerStatus = 'offline' | 'starting' | 'stopping' | 'running';
1212

@@ -23,7 +23,7 @@ const server: ServerDataStore = {
2323
permissions: [],
2424

2525
getServer: thunk(async (actions, payload) => {
26-
const [server, permissions] = await getServer(payload);
26+
const [ server, permissions ] = await getServer(payload);
2727

2828
actions.setServer(server);
2929
actions.setPermissions(permissions);
@@ -50,26 +50,6 @@ const status: ServerStatusStore = {
5050
}),
5151
};
5252

53-
interface ServerDatabaseStore {
54-
items: ServerDatabase[];
55-
setDatabases: Action<ServerDatabaseStore, ServerDatabase[]>;
56-
appendDatabase: Action<ServerDatabaseStore, ServerDatabase>;
57-
removeDatabase: Action<ServerDatabaseStore, ServerDatabase>;
58-
}
59-
60-
const databases: ServerDatabaseStore = {
61-
items: [],
62-
setDatabases: action((state, payload) => {
63-
state.items = payload;
64-
}),
65-
appendDatabase: action((state, payload) => {
66-
state.items = state.items.filter(item => item.id !== payload.id).concat(payload);
67-
}),
68-
removeDatabase: action((state, payload) => {
69-
state.items = state.items.filter(item => item.id !== payload.id);
70-
}),
71-
};
72-
7353
export interface ServerStore {
7454
server: ServerDataStore;
7555
subusers: ServerSubuserStore;
@@ -94,7 +74,7 @@ export const ServerContext = createContextStore<ServerStore>({
9474
clearServerState: action(state => {
9575
state.server.data = undefined;
9676
state.server.permissions = [];
97-
state.databases.items = [];
77+
state.databases.data = [];
9878
state.subusers.data = [];
9979
state.files.directory = '/';
10080
state.files.contents = [];

0 commit comments

Comments
 (0)