From 72f102a03446f0f7ac2aae637faceed0bb69c750 Mon Sep 17 00:00:00 2001
From: leonardo <leomilho@gmail.com>
Date: Tue, 25 Apr 2023 11:48:47 +0200
Subject: [PATCH] feat(navbar): partial navbar porting

---
 .../add-database-form.module.scss             |  75 +++++++
 .../navbar/AddDatabaseForm/index.tsx          | 205 ++++++++++++++++++
 libs/shared/lib/data-access/api/schema.ts     |   7 +
 .../lib/data-access/store/sessionSlice.ts     |  35 +++
 4 files changed, 322 insertions(+)
 create mode 100644 apps/web/src/components/navbar/AddDatabaseForm/add-database-form.module.scss
 create mode 100644 apps/web/src/components/navbar/AddDatabaseForm/index.tsx
 create mode 100644 libs/shared/lib/data-access/api/schema.ts
 create mode 100644 libs/shared/lib/data-access/store/sessionSlice.ts

diff --git a/apps/web/src/components/navbar/AddDatabaseForm/add-database-form.module.scss b/apps/web/src/components/navbar/AddDatabaseForm/add-database-form.module.scss
new file mode 100644
index 000000000..97642ae37
--- /dev/null
+++ b/apps/web/src/components/navbar/AddDatabaseForm/add-database-form.module.scss
@@ -0,0 +1,75 @@
+.wrapper {
+  height: 100vh;
+  display: flex;
+  align-items: center;
+  width: 100vw;
+  background-color: rgba(0, 0, 0, 0.87);
+  position: absolute;
+  z-index: 1225;
+}
+
+.authWrapper {
+  font-family: Poppins, sans-serif;
+  display: flex;
+  flex-direction: column;
+  flex-wrap: wrap;
+  justify-content: center;
+  max-width: 400px;
+  margin: auto;
+  overflow: auto;
+  min-height: 300px;
+  background-color: #f7f8fc;
+  padding: 30px;
+  border-radius: 5px;
+  padding-bottom: 300px;
+}
+
+.header {
+  text-align: center;
+}
+
+.formWrapper {
+}
+
+.loginContainer {
+  margin: 5px 0px !important;
+}
+
+.loginContainerRow {
+  display: flex;
+  flex-direction: row;
+  margin: 5px 0px;
+}
+
+.loginContainerButton {
+  margin-top: 10px !important;
+
+  & button {
+    width: 100%;
+  }
+}
+
+.hostLabel {
+  flex: 1 0 66.66667%;
+
+  & div {
+    width: 95%;
+  }
+}
+
+.portLabel {
+  flex: 1 0 33.33334%;
+}
+
+.userLabel {
+  width: 100%;
+}
+
+passLabel {
+  width: 100%;
+}
+
+cancelButton {
+  margin-top: 2.5%;
+}
+
diff --git a/apps/web/src/components/navbar/AddDatabaseForm/index.tsx b/apps/web/src/components/navbar/AddDatabaseForm/index.tsx
new file mode 100644
index 000000000..54c1b0ac0
--- /dev/null
+++ b/apps/web/src/components/navbar/AddDatabaseForm/index.tsx
@@ -0,0 +1,205 @@
+/**
+ * This program has been developed by students from the bachelor Computer Science at
+ * Utrecht University within the Software Project course.
+ * © Copyright Utrecht University (Department of Information and Computing Sciences)
+ */
+
+/* istanbul ignore file */
+/* The comment above was added so the code coverage wouldn't count this file towards code coverage.
+ * We do not test components/renderfunctions/styling files.
+ * See testing plan for more details.*/
+import React, { useState } from 'react';
+import { TextField, Button, NativeSelect } from '@mui/material';
+import styles from './add-database-form.module.scss';
+import {
+  AddDatabaseRequest,
+  DatabaseType,
+  databaseNameMapping,
+} from '@graphpolaris/shared/lib/data-access';
+
+/** AddDatabaseFormProps is an interface containing the AuthViewModel. */
+export interface AddDatabaseFormProps {
+  open: boolean;
+  onClose(): void;
+  onSubmit(data: AddDatabaseRequest): void;
+}
+
+/** AddDatabaseFormState is an interface containing the databasehost information. */
+export interface AddDatabaseFormState extends AddDatabaseRequest {
+  // username: string;
+  // password: string;
+  // hostname: string;
+  // port: number;
+  // databaseName: string;
+  // internalDatabase: string;
+  // databaseType: string;
+  // styles: Record<string, string>; // TODO: check if needed
+}
+
+/** AddDatabaseForm is the View implementation for the connect screen that will be rendered. */
+export default function AddDatabaseForm(props: AddDatabaseFormProps) {
+  const [state, setState] = useState<AddDatabaseFormState>({
+    username: 'root',
+    password: 'DikkeDraak',
+    url: 'https://datastrophe.science.uu.nl/',
+    port: 8529,
+    name: 'Tweede Kamer Dataset',
+    internal_database_name: 'TweedeKamer',
+    // styles: props.styles, // FIXME
+    type: DatabaseType.ArangoDB,
+  });
+
+  /**
+   * Validates if the port value is numerical. Only then will the state be updated.
+   * @param port The new port value.
+   */
+  function handlePortChanged(port: string): void {
+    if (!isNaN(Number(port))) setState({ ...state, port: Number(port) });
+  }
+
+  /** Handles the submit button click. Calls the onSubmit in the props with all the fields. */
+  function handleSubmitClicked(): void {
+    props.onSubmit(state);
+  }
+
+  return props.open ? (
+    <div
+      className={styles.wrapper}
+      onMouseDown={() => {
+        props.onClose();
+      }}
+    >
+      <div
+        className={styles.authWrapper}
+        onMouseDown={(e) => {
+          e.stopPropagation();
+        }}
+      >
+        <h1 className={styles.header}>Database Connect</h1>
+        <form
+          className={styles.formWrapper}
+          onSubmit={(event: React.FormEvent) => {
+            event.preventDefault();
+            handleSubmitClicked();
+          }}
+        >
+          <div className={styles.loginContainer}>
+            <TextField
+              className={styles.passLabel}
+              label="Database name"
+              type="databaseName"
+              value={state.name}
+              onChange={(event) =>
+                setState({
+                  ...state,
+                  name: event.currentTarget.value,
+                })
+              }
+              required
+            />
+          </div>
+          <div className={styles.loginContainer}>
+            <NativeSelect
+              className={styles.passLabel}
+              value={databaseNameMapping[state.type]}
+              onChange={(event) => {
+                setState({
+                  ...state,
+                  type: databaseNameMapping.indexOf(event.currentTarget.value),
+                });
+              }}
+            >
+              {databaseNameMapping.map((dbName) => (
+                <option value={dbName}>{dbName}</option>
+              ))}
+            </NativeSelect>
+          </div>
+          <div className={styles.loginContainerRow}>
+            <TextField
+              className={styles.hostLabel}
+              label="Hostname/IP"
+              type="hostname"
+              value={state.url}
+              onChange={(event) =>
+                setState({
+                  ...state,
+                  url: event.currentTarget.value,
+                })
+              }
+              required
+            />
+            <TextField
+              className={styles.portLabel}
+              label="Port"
+              type="port"
+              value={state.port}
+              onChange={(event) => handlePortChanged(event.currentTarget.value)}
+              required
+            />
+          </div>
+          <div className={styles.loginContainer}>
+            <TextField
+              className={styles.userLabel}
+              label="Username"
+              type="username"
+              value={state.username}
+              onChange={(event) =>
+                setState({
+                  ...state,
+                  username: event.currentTarget.value,
+                })
+              }
+              required
+            />
+          </div>
+          <div className={styles.loginContainer}>
+            <TextField
+              className={styles.passLabel}
+              label="Password"
+              type="password"
+              value={state.password}
+              onChange={(event) =>
+                setState({
+                  ...state,
+                  password: event.currentTarget.value,
+                })
+              }
+              required
+            />
+          </div>
+          <div className={styles.loginContainer}>
+            <TextField
+              className={styles.passLabel}
+              label="Internal database"
+              type="internalDatabaseName"
+              value={state.internal_database_name}
+              onChange={(event) =>
+                setState({
+                  ...state,
+                  internal_database_name: event.currentTarget.value,
+                })
+              }
+              required
+            />
+          </div>
+          <div className={styles.loginContainerButton}>
+            <Button variant="outlined" type="submit">
+              Submit
+            </Button>
+            <Button
+              className={styles.cancelButton}
+              variant="outlined"
+              onClick={() => {
+                props.onClose();
+              }}
+            >
+              Cancel
+            </Button>
+          </div>
+        </form>
+      </div>
+    </div>
+  ) : (
+    <></>
+  );
+}
diff --git a/libs/shared/lib/data-access/api/schema.ts b/libs/shared/lib/data-access/api/schema.ts
new file mode 100644
index 000000000..004a8b8e8
--- /dev/null
+++ b/libs/shared/lib/data-access/api/schema.ts
@@ -0,0 +1,7 @@
+// All database related API calls
+
+import { AuthorizationHandler } from '@graphpolaris/shared/lib/data-access/authorization';
+
+
+export function RequestSchema(databaseName: string) {
+}
\ No newline at end of file
diff --git a/libs/shared/lib/data-access/store/sessionSlice.ts b/libs/shared/lib/data-access/store/sessionSlice.ts
new file mode 100644
index 000000000..979f7f028
--- /dev/null
+++ b/libs/shared/lib/data-access/store/sessionSlice.ts
@@ -0,0 +1,35 @@
+import { createSlice, PayloadAction } from '@reduxjs/toolkit';
+import type { RootState } from './store';
+
+// Define the initial state using that type
+export const initialState: {
+  currentDatabase: string;
+  databases: string[];
+} = {
+  currentDatabase: '',
+  databases: [],
+};
+
+export const sessionSlice = createSlice({
+  name: 'session',
+  // `createSlice` will infer the state type from the `initialState` argument
+  initialState,
+  reducers: {
+    updateCurrentDatabase(state, action: PayloadAction<string>) {
+      state.currentDatabase = action.payload;
+    },
+    updateDatabaseList(state, action: PayloadAction<string[]>) {
+      state.databases = action.payload;
+    }
+  },
+});
+
+export const {
+  updateCurrentDatabase,
+  updateDatabaseList
+} = sessionSlice.actions;
+
+// Other code such as selectors can use the imported `RootState` type
+export const sessionCacheState = (state: RootState) => state.sessionCache;
+
+export default sessionSlice.reducer;
-- 
GitLab