diff --git a/README.md b/README.md
index 2f76b662..796c6f34 100644
--- a/README.md
+++ b/README.md
@@ -10,7 +10,7 @@
[![Github stars](https://img.shields.io/github/stars/oslabs-beta/SeeQR?style=social)](https://github.com/oslabs-beta/SeeQR)
[![Tests](https://github.com/open-source-labs/SeeQR/actions/workflows/test.yml/badge.svg)](https://github.com/open-source-labs/SeeQR/actions/workflows/test.yml)
-[theSeeQR.io](http://www.theseeqr.io)
+[theSeeQR.com](http://www.theseeqr.com)
SeeQR: A database analytic tool to compare the efficiency of different schemas and queries on a granular level so that developers/architects can make better informed architectural decisions regarding SQL databases at various scales.
@@ -56,7 +56,7 @@ To get started on contributing to this project:
- The application connects to the local instance of PostgreSQL using the role 'postgres', so all databases that 'postgres' has access to are available
- Besides using the existing databases, the application also provides various options to create new databases:
- Importing `.sql` or `.tar` files
- - Manually running `CREATE DATABASE` queries in SeeQR
+ - Navigating to the `Create Database` view at bottom of sidebar
- Copying an existing database (with or without original data)
- Users can toggle between the 'DATABASES' view and the 'QUERIES' view
@@ -81,7 +81,19 @@ To get started on contributing to this project:
+- Create/Edit Database
+ - Users can create a new database from scratch by clicking the `Create New Database` button at the bottom of the sidebar
+ - Once a the database is given a name, htiting the `Initialize Database` button will create new database on the users PostgreSQL instance
+ - Users can then input SQL commands and click `Update Database` to create and drop tables in the database
+ - Users have the option to alter any existing databases as well by selecting the database on the sidebar and running any SQL commands they would like.
+ - The `Export` button will write a .sql file on the user's desktop of the selected database
+
+
+
+
+
+
- Queries
- In the 'QUERIES' view, the main panel is where the query input text field is located, utilizing CodeMirror. The paint button in the top right corner of the panel auto-formats the inputted query
@@ -139,7 +151,7 @@ We've released SeeQR because it's a useful tool to help optimize SQL databases.
## Core Team
-[Casey Escovedo](https://github.com/caseyescovedo) | [Casey Walker](https://github.com/cwalker3011) | [Catherine Chiu](https://github.com/catherinechiu) | [Chris Akinrinade](https://github.com/chrisakinrinade) | [Cindy Chau](https://github.com/cindychau) | [Claudio Santos](https://github.com/Claudiohbsantos) | [Faraz Akhtar](https://github.com/faraza22) | [Frank Norton](https://github.com/FrankNorton32) | [James Kolotouros](https://github.com/dkolotouros) | [Jennifer Courtner](https://github.com/jcourtner) | [Justin Dury-Agri](https://github.com/justinD-A) | [Katie Klochan](https://github.com/kklochan) | [Mercer Stronck](https://github.com/mercerstronck) | [Muhammad Trad](https://github.com/muhammadtrad) | [Richard Lam](https://github.com/rlam108) | [Sam Frakes](https://github.com/frakes413) | [Serena Kuo](https://github.com/serenackuo)
+[Allison Le](https://github.com/allisonle1) | [Brandon Lee](https://github.com/BrandonW-Lee) | [Casey Escovedo](https://github.com/caseyescovedo) | [Casey Walker](https://github.com/cwalker3011) | [Catherine Chiu](https://github.com/catherinechiu) | [Chris Akinrinade](https://github.com/chrisakinrinade) | [Cindy Chau](https://github.com/cindychau) | [Claudio Santos](https://github.com/Claudiohbsantos) | [Faraz Akhtar](https://github.com/faraza22) | [Frank Norton](https://github.com/FrankNorton32) | [Harrison Nam](https://github.com/harrynam07) | [James Kolotouros](https://github.com/dkolotouros) | [Jennifer Courtner](https://github.com/jcourtner) | [Justin Dury-Agri](https://github.com/justinD-A) | [Katie Klochan](https://github.com/kklochan) | [Mercer Stronck](https://github.com/mercerstronck) | [Muhammad Trad](https://github.com/muhammadtrad) | [Richard Guo](https://github.com/richardguoo) | [Richard Lam](https://github.com/rlam108) | [Sam Frakes](https://github.com/frakes413) | [Serena Kuo](https://github.com/serenackuo) | [Timothy Sin](https://github.com/timothysin)
## License
diff --git a/assets/readmeImages/gifs/create_db.gif b/assets/readmeImages/gifs/create_db.gif
new file mode 100644
index 00000000..abbf3f99
Binary files /dev/null and b/assets/readmeImages/gifs/create_db.gif differ
diff --git a/assets/readmeImages/gifs/dummy_data.gif b/assets/readmeImages/gifs/dummy_data.gif
index 05a3cbe7..ed7b5254 100644
Binary files a/assets/readmeImages/gifs/dummy_data.gif and b/assets/readmeImages/gifs/dummy_data.gif differ
diff --git a/assets/readmeImages/gifs/quick_start.gif b/assets/readmeImages/gifs/quick_start.gif
index 4ecb6d43..3034a0ba 100644
Binary files a/assets/readmeImages/gifs/quick_start.gif and b/assets/readmeImages/gifs/quick_start.gif differ
diff --git a/frontend/components/App.tsx b/frontend/components/App.tsx
index aa3a22ba..2300c871 100644
--- a/frontend/components/App.tsx
+++ b/frontend/components/App.tsx
@@ -18,6 +18,7 @@ import QueryView from './views/QueryView/QueryView';
import DbView from './views/DbView/DbView';
import CompareView from './views/CompareView/CompareView';
import QuickStartView from './views/QuickStartView';
+import NewSchemaView from './views/NewSchemaView/NewSchemaView';
import FeedbackModal from './modal/FeedbackModal';
import Spinner from './modal/Spinner';
@@ -84,6 +85,9 @@ const App = () => {
}
shownView = 'queryView';
break;
+ case 'newSchemaView':
+ shownView = 'newSchemaView';
+ break;
case 'quickStartView':
default:
shownView = 'quickStartView';
@@ -128,6 +132,14 @@ const App = () => {
show={shownView === 'queryView'}
/>
+
diff --git a/frontend/components/sidebar/BottomButtons.tsx b/frontend/components/sidebar/BottomButtons.tsx
new file mode 100644
index 00000000..ed9703a5
--- /dev/null
+++ b/frontend/components/sidebar/BottomButtons.tsx
@@ -0,0 +1,56 @@
+import React from 'react';
+import { ButtonGroup, Button } from '@material-ui/core/';
+import styled from 'styled-components';
+import { ipcRenderer } from 'electron';
+import { AppState } from '../../types';
+import { selectedColor, textColor, defaultMargin } from '../../style-variables';
+import { sendFeedback } from '../../lib/utils';
+
+const ViewBtnGroup = styled(ButtonGroup)`
+ margin: ${defaultMargin} 5px;
+ position: fixed;
+ bottom: 0px;
+ width: 300px;
+`;
+
+interface ViewButtonProps {
+ $isSelected: boolean;
+}
+
+const ViewButton = styled(Button)`
+ background: ${({ $isSelected }: ViewButtonProps) =>
+ $isSelected ? selectedColor : textColor};
+
+`;
+
+
+type ViewSelectorProps = Pick;
+
+/**
+ * Selector for view on sidebar. Updates App state with selected view
+ */
+const BottomButtons = ({ selectedView, setSelectedView, setSelectedDb, selectedDb}: ViewSelectorProps) => (
+
+ {
+ setSelectedView('newSchemaView');
+ setSelectedDb('');
+
+ ipcRenderer
+ .invoke('select-db', '')
+ .catch(() =>
+ sendFeedback({
+ type: 'error',
+ message: `Database connection error`
+ })
+ )
+ }}
+ $isSelected={
+ selectedView === 'newSchemaView' || selectedView === 'compareView'
+ }
+ >
+ Create New Database
+
+
+);
+export default BottomButtons;
diff --git a/frontend/components/sidebar/DbList.tsx b/frontend/components/sidebar/DbList.tsx
index 5a458742..4ef1c38b 100644
--- a/frontend/components/sidebar/DbList.tsx
+++ b/frontend/components/sidebar/DbList.tsx
@@ -68,7 +68,7 @@ const DbList = ({
};
const selectHandler = (dbName: string) => {
- setSelectedView('dbView');
+ // setSelectedView('dbView');
if (dbName === selectedDb) return;
ipcRenderer
.invoke('select-db', dbName)
@@ -109,7 +109,7 @@ const DbList = ({
databases={databases}
/>
) : null}
-
+
{
+
const toggleOpen = () => setSidebarHidden(!sidebarIsHidden);
/**
* Show empty query view for user to create new query.
@@ -87,7 +90,7 @@ const Sidebar = ({
-
+
+ {/* */}
>
);
diff --git a/frontend/components/views/DbView/DbView.tsx b/frontend/components/views/DbView/DbView.tsx
index c79f62d9..48ab4bce 100644
--- a/frontend/components/views/DbView/DbView.tsx
+++ b/frontend/components/views/DbView/DbView.tsx
@@ -61,6 +61,7 @@ const DbView = ({ selectedDb, show }: DbViewProps) => {
setSelectedTable(table)}
selectedTable={selectedTable}
diff --git a/frontend/components/views/DbView/TablesTabBar.tsx b/frontend/components/views/DbView/TablesTabBar.tsx
index 9851cdb6..d5692012 100644
--- a/frontend/components/views/DbView/TablesTabBar.tsx
+++ b/frontend/components/views/DbView/TablesTabBar.tsx
@@ -1,4 +1,4 @@
-import React from 'react';
+import React, { useEffect }from 'react';
import { Tabs, Tab } from '@material-ui/core';
import styled from 'styled-components';
import TableDetails from './TableDetails';
@@ -33,7 +33,8 @@ const a11yProps = (index: any) => ({
'aria-controls': `scrollable-auto-tabpanel-${index}`,
});
-interface TablesTabBarProps {
+interface TablesTabBarProps {
+
tables: TableInfo[];
selectTable: (table: TableInfo) => void;
selectedTable: TableInfo | undefined;
@@ -44,6 +45,10 @@ const TablesTabs = ({
selectTable,
selectedTable,
}: TablesTabBarProps) => {
+
+
+
+
const handleChange = (event: React.ChangeEvent<{}>, newValue: number) => {
selectTable(tables[newValue]);
};
diff --git a/frontend/components/views/NewSchemaView/NewSchemaView.tsx b/frontend/components/views/NewSchemaView/NewSchemaView.tsx
new file mode 100644
index 00000000..7f4c8005
--- /dev/null
+++ b/frontend/components/views/NewSchemaView/NewSchemaView.tsx
@@ -0,0 +1,219 @@
+import { IpcRendererEvent, ipcRenderer } from 'electron';
+import React, { useEffect, useState } from 'react';
+import { Box, Button, Typography } from '@material-ui/core/';
+import styled from 'styled-components';
+import {
+ QueryData,
+ CreateNewQuery,
+ AppState,
+ TableInfo,
+ DatabaseInfo,
+ isDbLists
+} from '../../../types';
+import { defaultMargin } from '../../../style-variables';
+
+// not sure what this is yet...seems necessary for error message listeners
+import { once, sendFeedback } from '../../../lib/utils';
+
+// import child components below
+import SchemaName from './SchemaName';
+import TablesTabs from '../DbView/TablesTabBar';
+import SchemaSqlInput from './SchemaSqlInput';
+
+// emitting with no payload requests backend to send back a db-lists event with list of dbs
+const requestDbListOnce = once(() => ipcRenderer.send('return-db-list'));
+
+// top row container
+const TopRow = styled(Box)`
+ display: flex;
+ align-items: flex-end;
+ margin: ${defaultMargin} 0;
+`;
+
+// Container
+const Container = styled.a`
+ display: flex;
+ justify-content: flex-start;
+ padding-top: 0px;
+`;
+
+// button elements
+const CenterButton = styled(Box)`
+ display: flex;
+ justify-content: center;
+ padding-bottom: 0px;
+`;
+
+const RunButton = styled(Button)`
+ margin: ${defaultMargin} auto;
+`;
+
+const InitButton = styled(Button)`
+ margin-left: 2%;
+ height: 30px;
+ width: 160px;
+ font-size: 12px;
+`;
+
+const ExportButton = styled(Button)`
+ margin-left: 44%;
+ height: 30px;
+ width: 160px;
+ font-size: 12px;
+`;
+
+// view container
+const NewSchemaViewContainer = styled.div`
+ width: 100%;
+ height: 100%;
+ display: flex;
+ flex-direction: column;
+`;
+
+// props interface
+interface NewSchemaViewProps {
+ query?: AppState['workingQuery'];
+ setQuery: AppState['setWorkingQuery'];
+ createNewQuery: CreateNewQuery;
+ setSelectedDb: AppState['setSelectedDb'];
+ selectedDb: AppState['selectedDb'];
+ show: boolean;
+}
+
+const NewSchemaView = ({
+ query,
+ setQuery,
+ createNewQuery,
+ setSelectedDb,
+ selectedDb,
+ show,
+}: NewSchemaViewProps) => {
+ // additional local state properties using hooks
+ const [dbTables, setTables] = useState([]);
+ const [selectedTable, setSelectedTable] = useState();
+ const [currentSql, setCurrentSql] = useState('');
+ const [databases, setDatabases] = useState([]);
+ const [open, setOpen] = useState(false);
+
+
+ const defaultQuery: QueryData = {
+ label: '', // required by QueryData interface, but not necessary for this view
+ db: '', // name that user inputs in SchemaName.tsx
+ sqlString: '', // sql string that user inputs in SchemaSqlInput.tsx
+ };
+
+ const localQuery = { ...defaultQuery, ...query };
+
+ useEffect(() => {
+
+ // Listen to backend for updates to list of tables on current db
+ const tablesFromBackend = (evt: IpcRendererEvent, dbLists: unknown) => {
+
+ if (isDbLists(dbLists)) {
+ setDatabases(dbLists.databaseList);
+ setTables(dbLists.tableList);
+ setSelectedTable(selectedTable || dbLists.tableList[0]);
+ }
+ };
+ ipcRenderer.on('db-lists', tablesFromBackend);
+ requestDbListOnce();
+ // return cleanup function
+ return () => {
+ ipcRenderer.removeListener('db-lists', tablesFromBackend);
+ };
+ });
+
+ // handles naming of schema
+ const onNameChange = (newName: string) => {
+ setQuery({ ...localQuery, db: newName });
+ setSelectedDb(newName);
+ };
+
+ // handles sql string input
+ const onSqlChange = (newSql: string) => {
+ // because App's workingQuery changes ref
+ setCurrentSql(newSql);
+ setQuery({ ...localQuery, sqlString: newSql });
+ };
+
+ // handle intializing new schema
+ const onInitialize = () => {
+
+ ipcRenderer.invoke(
+ 'initialize-db', {
+ newDbName: localQuery.db,
+ })
+ .catch((err) => {
+ sendFeedback({
+ type: 'error',
+ message: err ?? 'Failed to initialize db',
+ });
+ });
+ }
+
+ // handle exporting
+ const onExport = () => {
+ ipcRenderer.invoke(
+ 'export-db', {
+ sourceDb: selectedDb
+ })
+ .catch((err) => {
+ sendFeedback({
+ type: 'error',
+ message: err ?? 'Failed to export db',
+ });
+ });
+ }
+
+
+ // onRun function to handle when user submits sql string to update schema
+ const onRun = () => {
+
+ setSelectedDb(localQuery.db);
+ // // request backend to run query
+ ipcRenderer
+ .invoke('update-db', {
+ sqlString: localQuery.sqlString,
+ selectedDb
+ })
+ .then(() => {setCurrentSql('');})
+ .catch((err) => {
+ sendFeedback({
+ type: 'error',
+ message: err ?? 'Failed to Update Schema',
+ });
+ });
+ };
+
+
+if (!show) return null;
+return (
+
+
+
+ Initialize Database
+ Export
+
+
+
+
+ Update Database
+
+
+
+ {`${selectedDb}`}
+
+ setSelectedTable(table)}
+ selectedTable={selectedTable}
+ />
+
+);
+};
+export default NewSchemaView;
\ No newline at end of file
diff --git a/frontend/components/views/NewSchemaView/SchemaExport.tsx b/frontend/components/views/NewSchemaView/SchemaExport.tsx
new file mode 100644
index 00000000..e69de29b
diff --git a/frontend/components/views/NewSchemaView/SchemaName.tsx b/frontend/components/views/NewSchemaView/SchemaName.tsx
new file mode 100644
index 00000000..ec3307f4
--- /dev/null
+++ b/frontend/components/views/NewSchemaView/SchemaName.tsx
@@ -0,0 +1,20 @@
+import React, { useState } from 'react';
+import { TextField, Box } from '@material-ui/core/';
+
+interface SchemaNameProps {
+ name? : string;
+ onChange: (newName: string) => void;
+}
+
+const SchemaName = ({ name, onChange }: SchemaNameProps) => (
+
+
+ onChange(evt.target.value)}
+ />
+
+);
+
+export default SchemaName;
diff --git a/frontend/components/views/NewSchemaView/SchemaSqlInput.tsx b/frontend/components/views/NewSchemaView/SchemaSqlInput.tsx
new file mode 100644
index 00000000..6bd294e2
--- /dev/null
+++ b/frontend/components/views/NewSchemaView/SchemaSqlInput.tsx
@@ -0,0 +1,74 @@
+import React from 'react';
+import FormatPaintIcon from '@material-ui/icons/FormatPaint';
+import { ButtonGroup, Button, Tooltip } from '@material-ui/core';
+import styled from 'styled-components';
+import { format } from 'sql-formatter';
+
+import 'codemirror/lib/codemirror.css'; // Styline
+import 'codemirror/mode/sql/sql'; // Language (Syntax Highlighting)
+import 'codemirror/theme/lesser-dark.css'; // Theme
+import CodeMirror from '@skidding/react-codemirror';
+
+const Container = styled.div`
+ position: relative;
+`;
+
+const SquareBtn = styled(Button)`
+ padding: 5px;
+`;
+
+const Toolbar = styled.div`
+ position: absolute;
+ z-index: 1000;
+ top: 5px;
+ right: 23px;
+ opacity: 0.3;
+ transition: opacity 0.3s ease;
+
+ &:hover {
+ opacity: 1;
+ }
+`;
+
+
+
+interface SchemaSqlInputProps {
+ sql: string;
+ onChange: (newSql: string) => void;
+ runQuery: () => void;
+ }
+
+ const SchemaSqlInput = ({ sql, onChange, runQuery }: SchemaSqlInputProps) => {
+ const formatQuery = () => {
+ const formatted = format(sql, { language: 'postgresql', uppercase: true });
+ onChange(formatted);
+ };
+
+ // Codemirror module configuration options
+ const options = {
+ lineNumbers: true,
+ mode: 'sql',
+ theme: 'lesser-dark',
+ extraKeys: {
+ 'Ctrl-Enter': runQuery,
+ 'Ctrl-F': formatQuery,
+ },
+ };
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+ };
+
+ export default SchemaSqlInput;
+
\ No newline at end of file
diff --git a/frontend/components/views/NewSchemaView/SchemaSummary.tsx b/frontend/components/views/NewSchemaView/SchemaSummary.tsx
new file mode 100644
index 00000000..e69de29b
diff --git a/frontend/components/views/QueryView/QuerySqlInput.tsx b/frontend/components/views/QueryView/QuerySqlInput.tsx
index 49c676a7..af8f1067 100644
--- a/frontend/components/views/QueryView/QuerySqlInput.tsx
+++ b/frontend/components/views/QueryView/QuerySqlInput.tsx
@@ -63,7 +63,7 @@ const QuerySqlInput = ({ sql, onChange, runQuery }: QuerySqlInputProps) => {
-
+
);
};
diff --git a/frontend/components/views/QueryView/QueryView.tsx b/frontend/components/views/QueryView/QueryView.tsx
index b667b396..b1f5ec9e 100644
--- a/frontend/components/views/QueryView/QueryView.tsx
+++ b/frontend/components/views/QueryView/QueryView.tsx
@@ -142,6 +142,9 @@ const QueryView = ({
};
createNewQuery(transformedData);
})
+ .then(() => {
+ localQuery.sqlString = '';
+ })
.catch((err) => {
sendFeedback({
type: 'error',
@@ -167,6 +170,7 @@ const QueryView = ({
diff --git a/frontend/components/views/QuickStartView.tsx b/frontend/components/views/QuickStartView.tsx
index 742b9f12..22378e13 100644
--- a/frontend/components/views/QuickStartView.tsx
+++ b/frontend/components/views/QuickStartView.tsx
@@ -253,7 +253,7 @@ const QuickStartView = ({ show }: QuickStartViewProps) => {
return (
- Welcome to SeeQr
+ Welcome to SeeQR
diff --git a/frontend/types.ts b/frontend/types.ts
index 04771df8..066b81c0 100644
--- a/frontend/types.ts
+++ b/frontend/types.ts
@@ -2,7 +2,7 @@
* This file contains common types that need to be used across the frontend
*/
-type ViewName = 'compareView' | 'dbView' | 'queryView' | 'quickStartView';
+type ViewName = 'compareView' | 'dbView' | 'queryView' | 'quickStartView' | 'newSchemaView';
export interface AppState {
selectedView: ViewName;