Skip to content
Merged
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
1 change: 1 addition & 0 deletions src/app.scss
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

// page styles
@import 'assets/styles/pages/instances';
@import 'assets/styles/pages/browse';
@import 'assets/styles/pages/organizations';
@import 'assets/styles/pages/organization';
@import 'assets/styles/pages/auth';
Expand Down
24 changes: 10 additions & 14 deletions src/assets/styles/components/_web-ide.scss
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
}

.name-input {

.validation-message {
color: $color-danger;
}
Expand Down Expand Up @@ -68,7 +67,6 @@
}

.purple {

button[disabled]:hover,
button[disabled] {
color: #5e5e5e !important;
Expand All @@ -94,16 +92,19 @@

color: white;

color: rgb(105, 105, 105);

.name-input {
.name-input-container {
input.invalid:focus {
border: 1px solid #ea4c89 !important;
outline-color: #ea4c89 !important;
}
}

.validation-message {
margin-top: 8px;
color: $color-danger;
margin-top: 8px;
}

& > input.invalid {
Expand All @@ -125,6 +126,7 @@
.editor-window .cancel-button {
color: rgb(105, 105, 105);;
}

.editor-window .cancel-button:hover {
color: white;
}
Expand All @@ -137,8 +139,6 @@
border-bottom: 1px solid lightgray;
}

color: rgb(105, 105, 105);


.default-window-option {
span {
Expand Down Expand Up @@ -199,13 +199,11 @@

.akamai,
.light {

.editor-window {
border: 1px solid $color-lightergrey;
}

.name-input {

.validation-message {
color: $color-danger;
}
Expand Down Expand Up @@ -295,8 +293,6 @@
}

.web-ide {


.cancel-button {
margin-left: 10px;
}
Expand Down Expand Up @@ -577,10 +573,10 @@
}

.name-input-container {
align-items: center;
display: flex;

flex-direction: row;
align-items: center;

.invalid-project-name {
margin-left: 15px;
Expand Down Expand Up @@ -735,16 +731,16 @@
}

.package-install-window {

.install-package-button {
width: 105px;
display: inline-flex;
align-items: center;
display: inline-flex;
justify-content: center;
width: 105px;

.install-package-status-icon {
}
}

&.github {
line-height:0.5em;
}
Expand Down Expand Up @@ -807,8 +803,8 @@
.project-name-invalid-text {

color: $color-danger !important;
margin-bottom: 20px;
display: block;
margin-bottom: 20px;
}


Expand Down
33 changes: 33 additions & 0 deletions src/assets/styles/pages/_browse.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#browse {
.missing-primary-key {
align-items: center;
display: flex;
flex-direction: column;
height: calc(100vh - 262px) !important;
justify-content: center;
line-height: 1.5 !important;

> .warning-text {
color: $color-warning !important;
}

&.narrow {
padding-left: 25%;
padding-right: 25%;
}

.card-body {
align-items: center;
display: flex;
flex-direction: column;
justify-content: center;
line-height: 1.5 !important;
padding-left: 25%;
padding-right: 25%;

.fa.fa-warning {
color: $color-danger;
}
}
}
}
72 changes: 36 additions & 36 deletions src/components/instance/browse/BrowseDatatable.js
Original file line number Diff line number Diff line change
Expand Up @@ -115,42 +115,42 @@ function BrowseDatatable({ tableState, setTableState, activeTable }) {

return (
<>
<DataTableHeader
totalRecords={totalRecords}
loading={loading}
loadingFilter={loadingFilter}
autoRefresh={tableState.autoRefresh}
refresh={() => setLastUpdate(Date.now())}
toggleAutoRefresh={() => setTableState({ ...tableState, autoRefresh: !tableState.autoRefresh })}
toggleFilter={() => setTableState({ ...tableState, showFilter: !tableState.showFilter })}
/>
<Card className="my-3">
<CardBody className="react-table-holder">
<DataTable
manual
columns={tableState.dataTableColumns || []}
data={tableState.tableData || []}
currentPage={tableState.page}
pageSize={tableState.pageSize}
totalPages={totalPages || 0}
showFilter={tableState.showFilter}
sorted={tableState.sorted.length ? tableState.sorted : [{ id: tableState.hashAttribute, desc: false }]}
loading={loading && !tableState.autoRefresh}
onFilteredChange={(value) => {
setTableState({ ...tableState, page: 0, filtered: value })
}}
onSortedChange={(value) => setTableState({ ...tableState, page: 0, sorted: value })}
onPageChange={(value) => setTableState({ ...tableState, page: value })}
onPageSizeChange={(value) => setTableState({ ...tableState, page: 0, pageSize: value })}
onRowClick={(rowData) => {
const hashValue = rowData[tableState.hashAttribute];
navigate(`/o/${customer_id}/i/${compute_stack_id}/browse/${schema}/${table}/edit/${rowData[tableState.hashAttribute]}`, {
state: { hashValue }
})
}}
/>
</CardBody>
</Card>
<DataTableHeader
totalRecords={totalRecords}
loading={loading}
loadingFilter={loadingFilter}
autoRefresh={tableState.autoRefresh}
refresh={() => setLastUpdate(Date.now())}
toggleAutoRefresh={() => setTableState({ ...tableState, autoRefresh: !tableState.autoRefresh })}
toggleFilter={() => setTableState({ ...tableState, showFilter: !tableState.showFilter })}
/>
<Card className="my-3">
<CardBody className="react-table-holder">
<DataTable
manual
columns={tableState.dataTableColumns || []}
data={tableState.tableData || []}
currentPage={tableState.page}
pageSize={tableState.pageSize}
totalPages={totalPages || 0}
showFilter={tableState.showFilter}
sorted={tableState.sorted.length ? tableState.sorted : [{ id: tableState.hashAttribute, desc: false }]}
loading={loading && !tableState.autoRefresh}
onFilteredChange={(value) => {
setTableState({ ...tableState, page: 0, filtered: value })
}}
onSortedChange={(value) => setTableState({ ...tableState, page: 0, sorted: value })}
onPageChange={(value) => setTableState({ ...tableState, page: value })}
onPageSizeChange={(value) => setTableState({ ...tableState, page: 0, pageSize: value })}
onRowClick={(rowData) => {
const hashValue = rowData[tableState.hashAttribute];
navigate(`/o/${customer_id}/i/${compute_stack_id}/browse/${schema}/${table}/edit/${rowData[tableState.hashAttribute]}`, {
state: { hashValue }
})
}}
/>
</CardBody>
</Card>
</>
);
}
Expand Down
58 changes: 46 additions & 12 deletions src/components/instance/browse/index.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import React, { useState, useEffect, lazy } from 'react';
import { Row, Col } from 'reactstrap';
import { Row, Col, Card, CardBody, CardTitle } from 'reactstrap';
import { useLocation, useParams, useNavigate } from 'react-router-dom';
import { useStoreState } from 'pullstate';
import { ErrorBoundary } from 'react-error-boundary';

import instanceState from '../../../functions/state/instanceState';
import describeTable from '../../../functions/api/instance/describeTable';

import ErrorFallback from '../../shared/ErrorFallback';
import addError from '../../../functions/api/lms/addError';
Expand All @@ -31,6 +32,20 @@ const defaultTableState = {
hashAttribute: false,
};

function NoPrimaryKeyMessage({ table }) {
return (
<Card className="my-3 missing-primary-key">
<CardBody>
<CardTitle>No Primary Key</CardTitle>
<i className="fa fa-warning mt-3" />
<span className="mt-3">
The table { `'${table}'` } does not have a primary key. The HarperDB Studio does not currently support tables wihtout a primary key defined. Please see the <a href="https://docs.harperdb.io/docs" target="_blank" rel="noreferrer">HarperDB documention</a> to see the standard HarperDB querying options.
</span>
</CardBody>
</Card>
);
}

function BrowseIndex() {
const navigate = useNavigate();
const location = useLocation();
Expand All @@ -51,16 +66,30 @@ function BrowseIndex() {
const emptyPromptMessage = showForm
? `Please ${(schema && entities.tables && !entities.tables.length) || !entities.schemas.length ? 'create' : 'choose'} a ${schema ? 'table' : `${versionAsFloat >= 4.2 ? 'database' : 'schema'}` }`
: "This user has not been granted access to any tables. A super-user must update this user's role.";
const [hasHashAttr, setHasHashAttr] = useState(true);

const syncInstanceStructure = () => {
buildInstanceStructure({ auth, url });
}

const checkForHashAttribute = () => {
async function check() {
if (table) {
const result = await describeTable({ auth, url, schema, table });
setHasHashAttr(Boolean(result.hash_attribute));
}
}

check();
};

useEffect(checkForHashAttribute, [auth, url, schema, table]);

const validate = () => {
if (structure) {

/*
* FIXME: There is a fair amount of logic scattered throughout this
* FIXME: There is a fair amount of logic scattered throughout this
* page that could be put in a router-level validation function.
*
* Splitting the browse endpoint into /schema/ and /schema/table heirarchy
Expand Down Expand Up @@ -102,7 +131,7 @@ function BrowseIndex() {
useEffect(syncInstanceStructure, [auth, url, schema, table]);

return (
<Row>
<Row id="browse">
<Col xl="3" lg="4" md="5" xs="12">
<ErrorBoundary
onError={(error, componentStack) => addError({ error: { message: error.message, componentStack } })}
Expand Down Expand Up @@ -132,15 +161,20 @@ function BrowseIndex() {
<ErrorBoundary
onError={(error, componentStack) => addError({ error: { message: error.message, componentStack } })}
FallbackComponent={ErrorFallback}>
{schema && table && action === 'csv' && entities.activeTable ? (
<CSVUpload />
) : schema && table && action && entities.activeTable ? (
<JSONEditor newEntityAttributes={tableState.newEntityAttributes} hashAttribute={tableState.hashAttribute} />
) : schema && table && entities.activeTable ? (
<DataTable activeTable={entities.activeTable} tableState={tableState} setTableState={setTableState} />
) : (
<EmptyPrompt headline={emptyPromptMessage} icon={<i className="fa fa-exclamation-triangle text-warning" />} />
)}
{
hasHashAttr ? (
schema && table && action === 'csv' && entities.activeTable ? (
<CSVUpload />
) : schema && table && action && entities.activeTable ? (
<JSONEditor newEntityAttributes={tableState.newEntityAttributes} hashAttribute={tableState.hashAttribute} />
) : schema && table && entities.activeTable ? (
<DataTable activeTable={entities.activeTable} tableState={tableState} setTableState={setTableState} />
) : (
<EmptyPrompt headline={emptyPromptMessage} icon={<i className="fa fa-exclamation-triangle text-warning" />} />
)
) :
<NoPrimaryKeyMessage table={table} />
}
</ErrorBoundary>
</Col>
</Row>
Expand Down
2 changes: 1 addition & 1 deletion src/functions/instance/getTableData.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ export default async ({ schema, table, filtered, pageSize, sorted, page, auth, u
if (allAttributes.includes('__createdtime__')) orderedColumns.push('__createdtime__');
if (allAttributes.includes('__updatedtime__')) orderedColumns.push('__updatedtime__');

const dataTableColumns = [ hashAttribute, ...orderedColumns ].map((k) => ({
const dataTableColumns = (hashAttribute ? [ hashAttribute, ...orderedColumns ] : [ ...orderedColumns ]).map((k) => ({
Header: k.toString(),
accessor: (row) => row[k.toString()],
}));
Expand Down