From 50aa46fcee6acdf8ebd94e61cd1a9bcf6c640b64 Mon Sep 17 00:00:00 2001 From: tillyw Date: Wed, 30 Oct 2024 19:08:19 -0500 Subject: [PATCH 01/85] adds moped_user_saved_views_to_database --- .../down.sql | 1 + .../up.sql | 25 +++++++++++++++++++ 2 files changed, 26 insertions(+) create mode 100644 moped-database/migrations/1730324794396_add_moped_user_saved_views_table/down.sql create mode 100644 moped-database/migrations/1730324794396_add_moped_user_saved_views_table/up.sql diff --git a/moped-database/migrations/1730324794396_add_moped_user_saved_views_table/down.sql b/moped-database/migrations/1730324794396_add_moped_user_saved_views_table/down.sql new file mode 100644 index 0000000000..fc02ddbb81 --- /dev/null +++ b/moped-database/migrations/1730324794396_add_moped_user_saved_views_table/down.sql @@ -0,0 +1 @@ +DROP TABLE moped_user_saved_views; diff --git a/moped-database/migrations/1730324794396_add_moped_user_saved_views_table/up.sql b/moped-database/migrations/1730324794396_add_moped_user_saved_views_table/up.sql new file mode 100644 index 0000000000..fd92361027 --- /dev/null +++ b/moped-database/migrations/1730324794396_add_moped_user_saved_views_table/up.sql @@ -0,0 +1,25 @@ +CREATE TABLE public.moped_user_saved_views ( + id serial NOT NULL, + description text, + url text NOT NULL, + query_filters jsonb, + created_by_user_id int4 NOT NULL, + updated_by_user_id int4 NOT NULL, + is_deleted boolean NOT NULL, + created_at timestamptz NOT NULL DEFAULT now(), + updated_at timestamptz NOT NULL DEFAULT now() +); + +ALTER TABLE moped_user_saved_views +ADD CONSTRAINT fk_moped_user_saved_views_created_by FOREIGN KEY (created_by_user_id) REFERENCES moped_users (user_id), +ADD CONSTRAINT fk_moped_user_saved_views_updated_by FOREIGN KEY (updated_by_user_id) REFERENCES moped_users (user_id); + +-- Adding comments for audit fields +COMMENT ON COLUMN moped_user_saved_views.created_at IS 'Timestamp of when the view was created'; +COMMENT ON COLUMN moped_user_saved_views.created_by_user_id IS 'User ID of the creator of the view'; +COMMENT ON COLUMN moped_user_saved_views.updated_by_user_id IS 'User ID of the last updater of the view'; +COMMENT ON COLUMN moped_user_saved_views.updated_at IS 'Timestamp of the last update of the view'; + +-- Adding comments for moped_user_saved_views constraints +COMMENT ON CONSTRAINT fk_moped_user_saved_views_created_by ON moped_user_saved_views IS 'Foreign key constraint linking created_by_user_id to moped_users table.'; +COMMENT ON CONSTRAINT fk_moped_user_saved_views_updated_by ON moped_user_saved_views IS 'Foreign key constraint linking updated_by_user_id to moped_users table.'; From 4ced18af17211836f92ba72eef65f463aa6f8343 Mon Sep 17 00:00:00 2001 From: tillyw Date: Wed, 30 Oct 2024 19:19:52 -0500 Subject: [PATCH 02/85] adds updated at trigger --- .../down.sql | 2 ++ .../1730324794396_add_moped_user_saved_views_table/up.sql | 7 +++++++ 2 files changed, 9 insertions(+) diff --git a/moped-database/migrations/1730324794396_add_moped_user_saved_views_table/down.sql b/moped-database/migrations/1730324794396_add_moped_user_saved_views_table/down.sql index fc02ddbb81..42b035448d 100644 --- a/moped-database/migrations/1730324794396_add_moped_user_saved_views_table/down.sql +++ b/moped-database/migrations/1730324794396_add_moped_user_saved_views_table/down.sql @@ -1 +1,3 @@ +DROP TRIGGER IF EXISTS set_moped_user_saved_views_updated_at ON moped_user_saved_views; + DROP TABLE moped_user_saved_views; diff --git a/moped-database/migrations/1730324794396_add_moped_user_saved_views_table/up.sql b/moped-database/migrations/1730324794396_add_moped_user_saved_views_table/up.sql index fd92361027..cc72b92783 100644 --- a/moped-database/migrations/1730324794396_add_moped_user_saved_views_table/up.sql +++ b/moped-database/migrations/1730324794396_add_moped_user_saved_views_table/up.sql @@ -23,3 +23,10 @@ COMMENT ON COLUMN moped_user_saved_views.updated_at IS 'Timestamp of the last up -- Adding comments for moped_user_saved_views constraints COMMENT ON CONSTRAINT fk_moped_user_saved_views_created_by ON moped_user_saved_views IS 'Foreign key constraint linking created_by_user_id to moped_users table.'; COMMENT ON CONSTRAINT fk_moped_user_saved_views_updated_by ON moped_user_saved_views IS 'Foreign key constraint linking updated_by_user_id to moped_users table.'; + +CREATE TRIGGER set_moped_user_saved_views_updated_at +BEFORE INSERT OR UPDATE ON moped_user_saved_views +FOR EACH ROW +EXECUTE FUNCTION public.set_updated_at(); + +COMMENT ON TRIGGER set_moped_user_saved_views_updated_at ON public.moped_user_saved_views IS 'Trigger to set updated_at timestamp for each insert or update on moped_user_saved_views'; From eb610724725f2a444353f148402d3e56fbd28563 Mon Sep 17 00:00:00 2001 From: tillyw Date: Wed, 30 Oct 2024 19:24:20 -0500 Subject: [PATCH 03/85] track table --- moped-database/metadata/tables.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/moped-database/metadata/tables.yaml b/moped-database/metadata/tables.yaml index 7e2050c8b5..df513d7f4b 100644 --- a/moped-database/metadata/tables.yaml +++ b/moped-database/metadata/tables.yaml @@ -5185,6 +5185,9 @@ - role: moped-viewer permission: filter: {} +- table: + name: moped_user_saved_views + schema: public - table: name: moped_users schema: public From 2e97dbfb6499764896ee35144ccd639331e0bc4c Mon Sep 17 00:00:00 2001 From: tillyw Date: Fri, 1 Nov 2024 18:47:13 -0500 Subject: [PATCH 04/85] adds permissions and triggers --- moped-database/metadata/tables.yaml | 138 ++++++++++++++++++++++++++++ 1 file changed, 138 insertions(+) diff --git a/moped-database/metadata/tables.yaml b/moped-database/metadata/tables.yaml index df513d7f4b..996dca5552 100644 --- a/moped-database/metadata/tables.yaml +++ b/moped-database/metadata/tables.yaml @@ -5188,6 +5188,144 @@ - table: name: moped_user_saved_views schema: public + insert_permissions: + - role: moped-admin + permission: + check: {} + set: + created_by_user_id: x-hasura-x-hasura-user-db-id + updated_by_user_id: x-hasura-x-hasura-user-db-id + columns: + - description + - is_deleted + - query_filters + - url + comment: "" + - role: moped-editor + permission: + check: {} + set: + created_by_user_id: x-hasura-x-hasura-user-db-id + updated_by_user_id: x-hasura-x-hasura-user-db-id + columns: + - description + - is_deleted + - query_filters + - url + comment: "" + select_permissions: + - role: moped-admin + permission: + columns: + - is_deleted + - id + - query_filters + - description + - url + - updated_at + - created_at + - created_by_user_id + - updated_by_user_id + filter: {} + comment: "" + - role: moped-editor + permission: + columns: + - is_deleted + - id + - query_filters + - description + - url + - updated_at + - created_at + - created_by_user_id + - updated_by_user_id + filter: {} + comment: "" + - role: moped-viewer + permission: + columns: + - is_deleted + - id + - query_filters + - description + - url + - updated_at + - created_at + - created_by_user_id + - updated_by_user_id + filter: {} + comment: "" + update_permissions: + - role: moped-admin + permission: + columns: + - description + - is_deleted + - query_filters + - url + filter: {} + check: null + set: + updated_by_user_id: x-hasura-x-hasura-user-db-id + comment: "" + - role: moped-editor + permission: + columns: + - description + - is_deleted + - query_filters + - url + filter: {} + check: null + set: + updated_by_user_id: x-hasura-x-hasura-user-db-id + comment: "" + event_triggers: + - name: activity_log_moped_user_saved_views + definition: + enable_manual: false + insert: + columns: '*' + update: + columns: '*' + retry_conf: + interval_sec: 10 + num_retries: 0 + timeout_sec: 60 + webhook_from_env: HASURA_ENDPOINT + headers: + - name: x-hasura-admin-secret + value_from_env: ACTIVITY_LOG_API_SECRET + request_transform: + body: + action: transform + template: |- + { + "query": "mutation InsertActivity($object: moped_activity_log_insert_input!) { insert_moped_activity_log_one(object: $object) { activity_id } }", + "variables": { + "object": { + "record_id": {{ $body.event.data.new.id }}, + "record_type": {{ $body.table.name }}, + "activity_id": {{ $body.id }}, + "record_data": {"event": {{ $body.event }}}, + "description": [{"newSchema": "true"}], + "operation_type": {{ $body.event.op }}, + "updated_by_user_id": {{ $session_variables?['x-hasura-user-db-id'] ?? 1}} + } + } + } + method: POST + query_params: {} + template_engine: Kriti + version: 2 + cleanup_config: + batch_size: 10000 + clean_invocation_logs: false + clear_older_than: 168 + paused: true + schedule: 0 0 * * * + timeout: 60 - table: name: moped_users schema: public From 75f30c8c70ea9936c4bcd0bf25820c9d14b76052 Mon Sep 17 00:00:00 2001 From: rose Date: Wed, 6 Nov 2024 13:35:45 -0600 Subject: [PATCH 05/85] create subprojects table toolbar --- .../ProjectSummary/SubprojectsToolbar.js | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectsToolbar.js diff --git a/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectsToolbar.js b/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectsToolbar.js new file mode 100644 index 0000000000..26f9346afe --- /dev/null +++ b/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectsToolbar.js @@ -0,0 +1,24 @@ +import AddCircleIcon from "@mui/icons-material/AddCircle"; +import Button from "@mui/material/Button"; +import Box from "@mui/material/Box"; +import Typography from "@mui/material/Typography"; +/** Custom toolbar title that resembles the material table titles we used */ +const SubprojectsToolbar = ({ onClick }) => ( + <> + + + Subprojects + + + + +); + +export default SubprojectsToolbar; From d7f0542e18947b8715fc900100cc0d9b6bae46c3 Mon Sep 17 00:00:00 2001 From: rose Date: Wed, 6 Nov 2024 13:35:56 -0600 Subject: [PATCH 06/85] use datagridpro --- .../ProjectSummary/SubprojectsTable.js | 287 +++++++++--------- 1 file changed, 149 insertions(+), 138 deletions(-) diff --git a/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectsTable.js b/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectsTable.js index d7b8283503..2426b5a3e8 100644 --- a/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectsTable.js +++ b/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectsTable.js @@ -14,11 +14,14 @@ import { DeleteOutline as DeleteOutlineIcon, EditOutlined as EditOutlinedIcon, } from "@mui/icons-material"; +import { DataGridPro } from "@mui/x-data-grid-pro"; +import dataGridProStyleOverrides from "src/styles/dataGridProStylesOverrides"; +import SubprojectsToolbar from "./SubprojectsToolbar"; import Autocomplete from "@mui/material/Autocomplete"; -import MaterialTable, { - MTableAction, - MTableToolbar, -} from "@material-table/core"; +// import MaterialTable, { +// MTableAction, +// MTableToolbar, +// } from "@material-table/core"; import ApolloErrorHandler from "../../../../components/ApolloErrorHandler"; import ProjectStatusBadge from "../../projectView/ProjectStatusBadge"; import RenderFieldLink from "../../../../components/RenderFieldLink"; @@ -46,155 +49,163 @@ const SubprojectsTable = ({ projectId = null, refetchSummaryData }) => { const columns = [ { - title: "ID", + headerName: "ID", field: "project_id", editable: "never", - width: "15%", + width: 50, }, { - title: "Full name", + headerName: "Full name", field: "project_name_full", - width: "40%", - validate: (entry) => (!!entry.project_name_full), - render: (entry) => ( - - ), - editComponent: (props) => ( - - - `${option.project_id} - ${option.project_name_full}` - } - value={props.value || null} - onChange={(event, value) => props.onChange(value)} - renderInput={(params) => ( - - )} - /> - Required - - ), + width: 350, + // validate: (entry) => !!entry.project_name_full, + // render: (entry) => ( + // + // ), + // editComponent: (props) => ( + // + // + // `${option.project_id} - ${option.project_name_full}` + // } + // value={props.value || null} + // onChange={(event, value) => props.onChange(value)} + // renderInput={(params) => ( + // + // )} + // /> + // Required + // + // ), }, { - title: "Status", + headerName: "Status", field: "status", editable: "never", - width: "25%", - customSort: (a, b) => - a.moped_proj_phases?.[0]?.moped_phase?.phase_name < - b.moped_proj_phases?.[0]?.moped_phase?.phase_name - ? -1 - : 1, - render: (entry) => ( - - ), + width: 200, + // customSort: (a, b) => + // a.moped_proj_phases?.[0]?.moped_phase?.phase_name < + // b.moped_proj_phases?.[0]?.moped_phase?.phase_name + // ? -1 + // : 1, + // render: (entry) => ( + // + // ), }, ]; + console.log(data.subprojects); + return ( - { - // If isn't the add action - if ( - typeof props.action === typeof Function || - props.action.tooltip !== "Add" - ) { - return ; - } else { - return ( - - ); - } - }, - Toolbar: (props) => ( - // to have it align with table content -
- -
- ), - }} - title={ - - Subprojects - - } - options={{ - paging: false, - search: false, - rowStyle: { fontFamily: typography.fontFamily }, - actionsColumnIndex: -1, - tableLayout: "fixed", - addRowPosition: "first", - idSynonym: "project_id", - }} - localization={{ - header: { - actions: "", - }, - body: { - emptyDataSourceMessage: ( - No subprojects to display - ), - editRow: { - deleteText: "Are you sure you want to remove this subproject?", - }, - }, - }} - icons={{ Delete: DeleteOutlineIcon, Edit: EditOutlinedIcon }} - editable={{ - onRowAdd: (newData) => { - const childProjectId = newData?.project_name_full?.project_id; - return updateProjectSubproject({ - variables: { - parentProjectId: projectId, - childProjectId: childProjectId, - }, - }) - .then(() => { - refetch(); - refetchSummaryData(); // Refresh subprojects in summary map - }) - .catch((error) => console.error(error)); - }, - onRowDelete: (newData) => { - const childProjectId = newData?.project_id; - return deleteProjectSubproject({ - variables: { - childProjectId: childProjectId, - }, - }) - .then(() => { - refetch(); - refetchSummaryData(); // Refresh subprojects in summary map - }) - .catch((error) => console.error(error)); - }, - }} + rows={data.subprojects ?? []} + getRowId={(row) => row.project_id} + slots={{ toolbar: SubprojectsToolbar }} + hideFooter + // data={data.subprojects ?? []} + // columns={columns} + // style={{ padding: "8px" }} + // components={{ + // // Note: in our other instances of Material Table, we bypass submitting the form on enter + // // In this table, since we currently only have one field to select, enter will submit the form + // Action: (props) => { + // // If isn't the add action + // if ( + // typeof props.action === typeof Function || + // props.action.tooltip !== "Add" + // ) { + // // return ; + // } else { + // return ( + // + // ); + // } + // }, + // Toolbar: (props) => ( + // // to have it align with table content + //
+ // {/* */} + //
+ // ), + // }} + // title={ + // + // Subprojects + // + // } + // options={{ + // paging: false, + // search: false, + // rowStyle: { fontFamily: typography.fontFamily }, + // actionsColumnIndex: -1, + // tableLayout: "fixed", + // addRowPosition: "first", + // idSynonym: "project_id", + // }} + // localization={{ + // header: { + // actions: "", + // }, + // body: { + // emptyDataSourceMessage: ( + // No subprojects to display + // ), + // editRow: { + // deleteText: "Are you sure you want to remove this subproject?", + // }, + // }, + // }} + // icons={{ Delete: DeleteOutlineIcon, Edit: EditOutlinedIcon }} + // editable={{ + // onRowAdd: (newData) => { + // const childProjectId = newData?.project_name_full?.project_id; + // return updateProjectSubproject({ + // variables: { + // parentProjectId: projectId, + // childProjectId: childProjectId, + // }, + // }) + // .then(() => { + // refetch(); + // refetchSummaryData(); // Refresh subprojects in summary map + // }) + // .catch((error) => console.error(error)); + // }, + // onRowDelete: (newData) => { + // const childProjectId = newData?.project_id; + // return deleteProjectSubproject({ + // variables: { + // childProjectId: childProjectId, + // }, + // }) + // .then(() => { + // refetch(); + // refetchSummaryData(); // Refresh subprojects in summary map + // }) + // .catch((error) => console.error(error)); + // }, + // }} />
); From 02e0ccc89e488f0579b8c82f2f211729a653d98b Mon Sep 17 00:00:00 2001 From: rose Date: Wed, 6 Nov 2024 18:04:57 -0600 Subject: [PATCH 07/85] prettier formatting --- moped-editor/src/queries/subprojects.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/moped-editor/src/queries/subprojects.js b/moped-editor/src/queries/subprojects.js index 586386b0ca..21e04e3ce9 100644 --- a/moped-editor/src/queries/subprojects.js +++ b/moped-editor/src/queries/subprojects.js @@ -3,7 +3,10 @@ import { gql } from "@apollo/client"; export const SUBPROJECT_QUERY = gql` query SubprojectSummary($projectId: Int) { subprojects: moped_project( - where: { parent_project_id: { _eq: $projectId }, is_deleted: { _eq: false } } + where: { + parent_project_id: { _eq: $projectId } + is_deleted: { _eq: false } + } ) { project_name project_name_full From a755850ea2b85fc8947c69b88e854dbd17341c2b Mon Sep 17 00:00:00 2001 From: rose Date: Wed, 6 Nov 2024 18:05:17 -0600 Subject: [PATCH 08/85] prettier formatting --- .../projectView/ProjectFunding/ProjectFundingTable.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/moped-editor/src/views/projects/projectView/ProjectFunding/ProjectFundingTable.js b/moped-editor/src/views/projects/projectView/ProjectFunding/ProjectFundingTable.js index e3ff47f75c..ae95dce4a8 100644 --- a/moped-editor/src/views/projects/projectView/ProjectFunding/ProjectFundingTable.js +++ b/moped-editor/src/views/projects/projectView/ProjectFunding/ProjectFundingTable.js @@ -92,8 +92,8 @@ const useStyles = makeStyles((theme) => ({ }, }, toolbarTitle: { - marginBottom: theme.spacing(1) - } + marginBottom: theme.spacing(1), + }, })); // memoized hook to concatanate fund dept and unit ids into an fdu string From 33319ef336359f441b594a2eee910e60131d3bd6 Mon Sep 17 00:00:00 2001 From: rose Date: Wed, 6 Nov 2024 18:06:02 -0600 Subject: [PATCH 09/85] hook for memoized columns, formatting the edit cell, startin to add the save/cancel buttons --- .../ProjectSummary/SubprojectsTable.js | 337 ++++++++++++++---- 1 file changed, 262 insertions(+), 75 deletions(-) diff --git a/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectsTable.js b/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectsTable.js index 2426b5a3e8..2de3916208 100644 --- a/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectsTable.js +++ b/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectsTable.js @@ -1,4 +1,4 @@ -import React from "react"; +import React, { useState, useEffect, useMemo, useCallback } from "react"; import { useQuery, useMutation } from "@apollo/client"; import { @@ -9,13 +9,21 @@ import { TextField, Typography, } from "@mui/material"; +import CheckIcon from "@mui/icons-material/Check"; +import CloseIcon from "@mui/icons-material/Close"; import { AddCircle as AddCircleIcon, DeleteOutline as DeleteOutlineIcon, EditOutlined as EditOutlinedIcon, } from "@mui/icons-material"; -import { DataGridPro } from "@mui/x-data-grid-pro"; +import { + DataGridPro, + GridRowModes, + GridActionsCellItem, + useGridApiContext, +} from "@mui/x-data-grid-pro"; import dataGridProStyleOverrides from "src/styles/dataGridProStylesOverrides"; +import { v4 as uuidv4 } from "uuid"; import SubprojectsToolbar from "./SubprojectsToolbar"; import Autocomplete from "@mui/material/Autocomplete"; // import MaterialTable, { @@ -33,6 +41,167 @@ import { } from "../../../../queries/subprojects"; import typography from "../../../../theme/typography"; +const SubprojectLookupComponent = ({ id, value, field, hasFocus, data }) => { + const apiRef = useGridApiContext(); + const ref = React.useRef(null); + + React.useEffect(() => { + if (hasFocus) { + ref.current.focus(); + } + }, [hasFocus]); + + const handleChange = (event, newValue) => { + apiRef.current.setEditCellValue({ + id, + field, + value: newValue ? newValue : null, + }); + }; + + return ( + + + `${option.project_id} - ${option.project_name_full}` + } + value={value || null} + onChange={handleChange} + renderInput={(params) => } + /> + Required + + ); +}; +/** Hook that provides memoized column settings */ +const useColumns = ({ + data, + rowModesModel, + handleDeleteOpen, + handleSaveClick, + handleCancelClick, + handleEditClick, +}) => + useMemo(() => { + console.log(data); + return [ + { + headerName: "ID", + field: "project_id", + editable: false, + width: 50, + }, + { + headerName: "Full name", + field: "project_name_full", + editable: true, + width: 350, + renderEditCell: (props) => ( + + ), + // validate: (entry) => !!entry.project_name_full, + // render: (entry) => ( + // + // ), + // editComponent: (props) => ( + // + // + // `${option.project_id} - ${option.project_name_full}` + // } + // value={props.value || null} + // onChange={(event, value) => props.onChange(value)} + // renderInput={(params) => ( + // + // )} + // /> + // Required + // + // ), + }, + { + headerName: "Status", + field: "status", + editable: false, + width: 200, + // customSort: (a, b) => + // a.moped_proj_phases?.[0]?.moped_phase?.phase_name < + // b.moped_proj_phases?.[0]?.moped_phase?.phase_name + // ? -1 + // : 1, + // render: (entry) => ( + // + // ), + }, + { + headerName: "", + field: "edit", + hideable: false, + filterable: false, + sortable: false, + editable: false, + type: "actions", + getActions: ({ id }) => { + const isInEditMode = rowModesModel[id]?.mode === GridRowModes.Edit; + if (isInEditMode) { + return [ + } + label="Save" + sx={{ + color: "primary.main", + }} + onClick={handleSaveClick(id)} + />, + // } + // label="Cancel" + // className="textPrimary" + // onClick={handleCancelClick(id)} + // color="inherit" + // />, + ]; + } + // return [ + // } + // label="Edit" + // className="textPrimary" + // onClick={handleEditClick(id)} + // color="inherit" + // />, + // } + // label="Delete" + // onClick={() => handleDeleteOpen(id)} + // color="inherit" + // />, + // ]; + }, + }, + ]; + }, [ + data, + rowModesModel, + // handleDeleteOpen, + handleSaveClick, + // handleCancelClick, + // handleEditClick, + ]); + const SubprojectsTable = ({ projectId = null, refetchSummaryData }) => { const addActionRef = React.useRef(); @@ -44,76 +213,94 @@ const SubprojectsTable = ({ projectId = null, refetchSummaryData }) => { const [updateProjectSubproject] = useMutation(UPDATE_PROJECT_SUBPROJECT); const [deleteProjectSubproject] = useMutation(DELETE_PROJECT_SUBPROJECT); + const [rows, setRows] = useState([]); + const [rowModesModel, setRowModesModel] = useState({}); + + useEffect(() => { + if (data && data.subprojects.length > 0) { + setRows(data.subprojects); + } + }, [data]); + if (error) console.error(error); - if (loading || !data) return ; - - const columns = [ - { - headerName: "ID", - field: "project_id", - editable: "never", - width: 50, - }, - { - headerName: "Full name", - field: "project_name_full", - width: 350, - // validate: (entry) => !!entry.project_name_full, - // render: (entry) => ( - // - // ), - // editComponent: (props) => ( - // - // - // `${option.project_id} - ${option.project_name_full}` - // } - // value={props.value || null} - // onChange={(event, value) => props.onChange(value)} - // renderInput={(params) => ( - // - // )} - // /> - // Required - // - // ), - }, - { - headerName: "Status", - field: "status", - editable: "never", - width: 200, - // customSort: (a, b) => - // a.moped_proj_phases?.[0]?.moped_phase?.phase_name < - // b.moped_proj_phases?.[0]?.moped_phase?.phase_name - // ? -1 - // : 1, - // render: (entry) => ( - // - // ), + if (loading || !data); + + const handleAddSubprojectClick = () => { + // use a random id to keep track of row in row modes model and data grid rows + // before the record is added to the db + const id = uuidv4(); + setRows((oldRows) => [ + { + id, + project_id: id, + project_name_full: null, + status: null, + isNew: true, + }, + ...oldRows, + ]); + setRowModesModel((oldModel) => ({ + ...oldModel, + [id]: { mode: GridRowModes.Edit, fieldToFocus: "project_name_full" }, + })); + }; + + const handleSaveClick = useCallback( + (id) => () => { + setRowModesModel({ ...rowModesModel, [id]: { mode: GridRowModes.View } }); }, - ]; + [rowModesModel] + ); - console.log(data.subprojects); + const processRowUpdate = (updatedRow) => { + // const updatedSubprojectData = updatedRow; + + // delete updatedSubprojectData.isNew; + // delete updatedSubprojectData.id; + + // updateProjectSubproject({ + // variables: { + // objects: { + // ...updatedSubprojectData, + // }, + // }, + // }); + + const childProjectId = updatedRow?.project_name_full?.project_id; + return updateProjectSubproject({ + variables: { + parentProjectId: projectId, + childProjectId: childProjectId, + }, + }) + .then(() => { + refetch(); + refetchSummaryData(); // Refresh subprojects in summary map + }) + .catch((error) => console.error(error)); + }; + + const dataGridColumns = useColumns({ + data, + rowModesModel, + // handleDeleteOpen, + handleSaveClick, + // handleCancelClick, + // handleEditClick, + }); return ( row.project_id} + rowModesModel={rowModesModel} slots={{ toolbar: SubprojectsToolbar }} + slotProps={{ toolbar: { onClick: handleAddSubprojectClick } }} + editMode="row" + processRowUpdate={processRowUpdate} hideFooter // data={data.subprojects ?? []} // columns={columns} @@ -178,20 +365,20 @@ const SubprojectsTable = ({ projectId = null, refetchSummaryData }) => { // }} // icons={{ Delete: DeleteOutlineIcon, Edit: EditOutlinedIcon }} // editable={{ - // onRowAdd: (newData) => { - // const childProjectId = newData?.project_name_full?.project_id; - // return updateProjectSubproject({ - // variables: { - // parentProjectId: projectId, - // childProjectId: childProjectId, - // }, + // onRowAdd: (newData) => { + // const childProjectId = newData?.project_name_full?.project_id; + // return updateProjectSubproject({ + // variables: { + // parentProjectId: projectId, + // childProjectId: childProjectId, + // }, + // }) + // .then(() => { + // refetch(); + // refetchSummaryData(); // Refresh subprojects in summary map // }) - // .then(() => { - // refetch(); - // refetchSummaryData(); // Refresh subprojects in summary map - // }) - // .catch((error) => console.error(error)); - // }, + // .catch((error) => console.error(error)); + // }, // onRowDelete: (newData) => { // const childProjectId = newData?.project_id; // return deleteProjectSubproject({ From b53c2238fc95355f764f4982ce45bd4499a03e4a Mon Sep 17 00:00:00 2001 From: tillyw Date: Thu, 7 Nov 2024 13:27:29 -0600 Subject: [PATCH 10/85] addressed requested changes --- moped-database/metadata/tables.yaml | 45 ------------------- .../up.sql | 13 ++++-- 2 files changed, 9 insertions(+), 49 deletions(-) diff --git a/moped-database/metadata/tables.yaml b/moped-database/metadata/tables.yaml index 996dca5552..b9be1b09cf 100644 --- a/moped-database/metadata/tables.yaml +++ b/moped-database/metadata/tables.yaml @@ -5281,51 +5281,6 @@ set: updated_by_user_id: x-hasura-x-hasura-user-db-id comment: "" - event_triggers: - - name: activity_log_moped_user_saved_views - definition: - enable_manual: false - insert: - columns: '*' - update: - columns: '*' - retry_conf: - interval_sec: 10 - num_retries: 0 - timeout_sec: 60 - webhook_from_env: HASURA_ENDPOINT - headers: - - name: x-hasura-admin-secret - value_from_env: ACTIVITY_LOG_API_SECRET - request_transform: - body: - action: transform - template: |- - { - "query": "mutation InsertActivity($object: moped_activity_log_insert_input!) { insert_moped_activity_log_one(object: $object) { activity_id } }", - "variables": { - "object": { - "record_id": {{ $body.event.data.new.id }}, - "record_type": {{ $body.table.name }}, - "activity_id": {{ $body.id }}, - "record_data": {"event": {{ $body.event }}}, - "description": [{"newSchema": "true"}], - "operation_type": {{ $body.event.op }}, - "updated_by_user_id": {{ $session_variables?['x-hasura-user-db-id'] ?? 1}} - } - } - } - method: POST - query_params: {} - template_engine: Kriti - version: 2 - cleanup_config: - batch_size: 10000 - clean_invocation_logs: false - clear_older_than: 168 - paused: true - schedule: 0 0 * * * - timeout: 60 - table: name: moped_users schema: public diff --git a/moped-database/migrations/1730324794396_add_moped_user_saved_views_table/up.sql b/moped-database/migrations/1730324794396_add_moped_user_saved_views_table/up.sql index cc72b92783..f63323a706 100644 --- a/moped-database/migrations/1730324794396_add_moped_user_saved_views_table/up.sql +++ b/moped-database/migrations/1730324794396_add_moped_user_saved_views_table/up.sql @@ -1,13 +1,13 @@ CREATE TABLE public.moped_user_saved_views ( id serial NOT NULL, - description text, + description text NOT NULL, url text NOT NULL, query_filters jsonb, created_by_user_id int4 NOT NULL, updated_by_user_id int4 NOT NULL, - is_deleted boolean NOT NULL, created_at timestamptz NOT NULL DEFAULT now(), - updated_at timestamptz NOT NULL DEFAULT now() + updated_at timestamptz NOT NULL DEFAULT now(), + is_deleted boolean NOT NULL DEFAULT false ); ALTER TABLE moped_user_saved_views @@ -15,10 +15,15 @@ ADD CONSTRAINT fk_moped_user_saved_views_created_by FOREIGN KEY (created_by_user ADD CONSTRAINT fk_moped_user_saved_views_updated_by FOREIGN KEY (updated_by_user_id) REFERENCES moped_users (user_id); -- Adding comments for audit fields -COMMENT ON COLUMN moped_user_saved_views.created_at IS 'Timestamp of when the view was created'; +-- TO DO: add comments to all fields +COMMENT ON COLUMN moped_user_saved_views.description IS 'Description entered by the creator of the view'; +COMMENT ON COLUMN moped_user_saved_views.url IS 'URL string associated with the view (may break if database fields or operators are changed)'; +COMMENT ON COLUMN moped_user_saved_views.query_filters IS 'JSON blob of filters that make up the query'; COMMENT ON COLUMN moped_user_saved_views.created_by_user_id IS 'User ID of the creator of the view'; COMMENT ON COLUMN moped_user_saved_views.updated_by_user_id IS 'User ID of the last updater of the view'; +COMMENT ON COLUMN moped_user_saved_views.created_at IS 'Timestamp of when the view was created'; COMMENT ON COLUMN moped_user_saved_views.updated_at IS 'Timestamp of the last update of the view'; +COMMENT ON COLUMN moped_user_saved_views.query_filters IS 'Boolean indicating whether the view has been soft deleted and thereby not rendered in the UI'; -- Adding comments for moped_user_saved_views constraints COMMENT ON CONSTRAINT fk_moped_user_saved_views_created_by ON moped_user_saved_views IS 'Foreign key constraint linking created_by_user_id to moped_users table.'; From 7508871848232c41c4dce4c20c24ac0d5381de68 Mon Sep 17 00:00:00 2001 From: tillyw Date: Thu, 7 Nov 2024 13:35:56 -0600 Subject: [PATCH 11/85] remove comment --- .../1730324794396_add_moped_user_saved_views_table/up.sql | 1 - 1 file changed, 1 deletion(-) diff --git a/moped-database/migrations/1730324794396_add_moped_user_saved_views_table/up.sql b/moped-database/migrations/1730324794396_add_moped_user_saved_views_table/up.sql index f63323a706..682d346f2c 100644 --- a/moped-database/migrations/1730324794396_add_moped_user_saved_views_table/up.sql +++ b/moped-database/migrations/1730324794396_add_moped_user_saved_views_table/up.sql @@ -15,7 +15,6 @@ ADD CONSTRAINT fk_moped_user_saved_views_created_by FOREIGN KEY (created_by_user ADD CONSTRAINT fk_moped_user_saved_views_updated_by FOREIGN KEY (updated_by_user_id) REFERENCES moped_users (user_id); -- Adding comments for audit fields --- TO DO: add comments to all fields COMMENT ON COLUMN moped_user_saved_views.description IS 'Description entered by the creator of the view'; COMMENT ON COLUMN moped_user_saved_views.url IS 'URL string associated with the view (may break if database fields or operators are changed)'; COMMENT ON COLUMN moped_user_saved_views.query_filters IS 'JSON blob of filters that make up the query'; From f0bda1df7f4d04b9b1c371200533f56f54ffe58c Mon Sep 17 00:00:00 2001 From: rose Date: Fri, 8 Nov 2024 11:19:22 -0600 Subject: [PATCH 12/85] create separate subproject lookup component --- .../ProjectSummary/SubprojectsTable.js | 93 +++++++------------ 1 file changed, 35 insertions(+), 58 deletions(-) diff --git a/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectsTable.js b/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectsTable.js index 2de3916208..2db597abcd 100644 --- a/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectsTable.js +++ b/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectsTable.js @@ -33,6 +33,7 @@ import Autocomplete from "@mui/material/Autocomplete"; import ApolloErrorHandler from "../../../../components/ApolloErrorHandler"; import ProjectStatusBadge from "../../projectView/ProjectStatusBadge"; import RenderFieldLink from "../../../../components/RenderFieldLink"; +import SubprojectLookupComponent from "./SubprojectLookupComponent"; import { SUBPROJECT_QUERY, @@ -41,41 +42,6 @@ import { } from "../../../../queries/subprojects"; import typography from "../../../../theme/typography"; -const SubprojectLookupComponent = ({ id, value, field, hasFocus, data }) => { - const apiRef = useGridApiContext(); - const ref = React.useRef(null); - - React.useEffect(() => { - if (hasFocus) { - ref.current.focus(); - } - }, [hasFocus]); - - const handleChange = (event, newValue) => { - apiRef.current.setEditCellValue({ - id, - field, - value: newValue ? newValue : null, - }); - }; - - return ( - - - `${option.project_id} - ${option.project_name_full}` - } - value={value || null} - onChange={handleChange} - renderInput={(params) => } - /> - Required - - ); -}; /** Hook that provides memoized column settings */ const useColumns = ({ data, @@ -100,7 +66,7 @@ const useColumns = ({ editable: true, width: 350, renderEditCell: (props) => ( - + ), // validate: (entry) => !!entry.project_name_full, // render: (entry) => ( @@ -133,17 +99,24 @@ const useColumns = ({ field: "status", editable: false, width: 200, + renderCell: ({ value }) => { + ; + }, // customSort: (a, b) => // a.moped_proj_phases?.[0]?.moped_phase?.phase_name < // b.moped_proj_phases?.[0]?.moped_phase?.phase_name // ? -1 // : 1, // render: (entry) => ( - // + // // ), }, { @@ -175,21 +148,21 @@ const useColumns = ({ // />, ]; } - // return [ - // } - // label="Edit" - // className="textPrimary" - // onClick={handleEditClick(id)} - // color="inherit" - // />, - // } - // label="Delete" - // onClick={() => handleDeleteOpen(id)} - // color="inherit" - // />, - // ]; + return [ + } + label="Edit" + className="textPrimary" + // onClick={handleEditClick(id)} + color="inherit" + />, + // } + // label="Delete" + // onClick={() => handleDeleteOpen(id)} + // color="inherit" + // />, + ]; }, }, ]; @@ -203,8 +176,6 @@ const useColumns = ({ ]); const SubprojectsTable = ({ projectId = null, refetchSummaryData }) => { - const addActionRef = React.useRef(); - const { loading, error, data, refetch } = useQuery(SUBPROJECT_QUERY, { variables: { projectId: projectId }, fetchPolicy: "no-cache", @@ -226,6 +197,7 @@ const SubprojectsTable = ({ projectId = null, refetchSummaryData }) => { if (loading || !data); const handleAddSubprojectClick = () => { + console.log("subproject clicked"); // use a random id to keep track of row in row modes model and data grid rows // before the record is added to the db const id = uuidv4(); @@ -252,6 +224,10 @@ const SubprojectsTable = ({ projectId = null, refetchSummaryData }) => { [rowModesModel] ); + const handleRowModesModelChange = (newRowModesModel) => { + setRowModesModel(newRowModesModel); + }; + const processRowUpdate = (updatedRow) => { // const updatedSubprojectData = updatedRow; @@ -297,6 +273,7 @@ const SubprojectsTable = ({ projectId = null, refetchSummaryData }) => { rows={rows} getRowId={(row) => row.project_id} rowModesModel={rowModesModel} + onRowModesModelChange={handleRowModesModelChange} slots={{ toolbar: SubprojectsToolbar }} slotProps={{ toolbar: { onClick: handleAddSubprojectClick } }} editMode="row" From 891980e20c741d5ce742b81c787da738af53a484 Mon Sep 17 00:00:00 2001 From: rose Date: Fri, 8 Nov 2024 11:19:30 -0600 Subject: [PATCH 13/85] create separate subproject lookup component --- .../SubprojectLookupComponent.js | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectLookupComponent.js diff --git a/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectLookupComponent.js b/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectLookupComponent.js new file mode 100644 index 0000000000..9523f59c40 --- /dev/null +++ b/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectLookupComponent.js @@ -0,0 +1,45 @@ +import React from "react"; +import { useGridApiContext } from "@mui/x-data-grid-pro"; +import { FormControl, FormHelperText, TextField } from "@mui/material"; +import Autocomplete from "@mui/material/Autocomplete"; + +const SubprojectLookupComponent = ({ id, value, field, hasFocus, data }) => { + const apiRef = useGridApiContext(); + const ref = React.useRef(null); + + console.log(hasFocus, ref, ref.current, "Test"); + + // React.useEffect(() => { + // if (hasFocus) { + // ref.current.focus(); + // console.log("we are focusing"); + // } + // }, [hasFocus]); + + const handleChange = (event, newValue) => { + apiRef.current.setEditCellValue({ + id, + field, + value: newValue ? newValue : null, + }); + }; + + return ( + + + `${option.project_id} - ${option.project_name_full}` + } + value={value || null} + onChange={handleChange} + renderInput={(params) => } + /> + Required + + ); +}; + +export default SubprojectLookupComponent; From c9b04c76d993f563fd1a833c6f3dc260c9f4ce65 Mon Sep 17 00:00:00 2001 From: rose Date: Fri, 8 Nov 2024 12:51:47 -0600 Subject: [PATCH 14/85] add functionality for cancel click and delete modal --- .../ProjectSummary/SubprojectsTable.js | 259 +++++------------- 1 file changed, 66 insertions(+), 193 deletions(-) diff --git a/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectsTable.js b/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectsTable.js index 2db597abcd..af60935607 100644 --- a/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectsTable.js +++ b/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectsTable.js @@ -1,46 +1,27 @@ import React, { useState, useEffect, useMemo, useCallback } from "react"; import { useQuery, useMutation } from "@apollo/client"; -import { - Button, - CircularProgress, - FormControl, - FormHelperText, - TextField, - Typography, -} from "@mui/material"; import CheckIcon from "@mui/icons-material/Check"; import CloseIcon from "@mui/icons-material/Close"; -import { - AddCircle as AddCircleIcon, - DeleteOutline as DeleteOutlineIcon, - EditOutlined as EditOutlinedIcon, -} from "@mui/icons-material"; +import { DeleteOutline as DeleteOutlineIcon } from "@mui/icons-material"; import { DataGridPro, GridRowModes, GridActionsCellItem, - useGridApiContext, } from "@mui/x-data-grid-pro"; import dataGridProStyleOverrides from "src/styles/dataGridProStylesOverrides"; import { v4 as uuidv4 } from "uuid"; import SubprojectsToolbar from "./SubprojectsToolbar"; -import Autocomplete from "@mui/material/Autocomplete"; -// import MaterialTable, { -// MTableAction, -// MTableToolbar, -// } from "@material-table/core"; import ApolloErrorHandler from "../../../../components/ApolloErrorHandler"; import ProjectStatusBadge from "../../projectView/ProjectStatusBadge"; -import RenderFieldLink from "../../../../components/RenderFieldLink"; import SubprojectLookupComponent from "./SubprojectLookupComponent"; +import DeleteConfirmationModal from "../DeleteConfirmationModal"; import { SUBPROJECT_QUERY, UPDATE_PROJECT_SUBPROJECT, DELETE_PROJECT_SUBPROJECT, } from "../../../../queries/subprojects"; -import typography from "../../../../theme/typography"; /** Hook that provides memoized column settings */ const useColumns = ({ @@ -49,7 +30,6 @@ const useColumns = ({ handleDeleteOpen, handleSaveClick, handleCancelClick, - handleEditClick, }) => useMemo(() => { console.log(data); @@ -58,66 +38,29 @@ const useColumns = ({ headerName: "ID", field: "project_id", editable: false, - width: 50, + width: 75, }, { headerName: "Full name", field: "project_name_full", editable: true, - width: 350, + width: 250, renderEditCell: (props) => ( ), - // validate: (entry) => !!entry.project_name_full, - // render: (entry) => ( - // - // ), - // editComponent: (props) => ( - // - // - // `${option.project_id} - ${option.project_name_full}` - // } - // value={props.value || null} - // onChange={(event, value) => props.onChange(value)} - // renderInput={(params) => ( - // - // )} - // /> - // Required - // - // ), }, { headerName: "Status", field: "status", editable: false, width: 200, - renderCell: ({ value }) => { + renderCell: ({ row }) => ( ; - }, - // customSort: (a, b) => - // a.moped_proj_phases?.[0]?.moped_phase?.phase_name < - // b.moped_proj_phases?.[0]?.moped_phase?.phase_name - // ? -1 - // : 1, - // render: (entry) => ( - // - // ), + /> + ), }, { headerName: "", @@ -139,29 +82,22 @@ const useColumns = ({ }} onClick={handleSaveClick(id)} />, - // } - // label="Cancel" - // className="textPrimary" - // onClick={handleCancelClick(id)} - // color="inherit" - // />, + } + label="Cancel" + className="textPrimary" + onClick={handleCancelClick(id)} + color="inherit" + />, ]; } return [ } - label="Edit" - className="textPrimary" - // onClick={handleEditClick(id)} + icon={} + label="Delete" + onClick={() => handleDeleteOpen(id)} color="inherit" />, - // } - // label="Delete" - // onClick={() => handleDeleteOpen(id)} - // color="inherit" - // />, ]; }, }, @@ -169,10 +105,9 @@ const useColumns = ({ }, [ data, rowModesModel, - // handleDeleteOpen, + handleDeleteOpen, handleSaveClick, - // handleCancelClick, - // handleEditClick, + handleCancelClick, ]); const SubprojectsTable = ({ projectId = null, refetchSummaryData }) => { @@ -186,6 +121,9 @@ const SubprojectsTable = ({ projectId = null, refetchSummaryData }) => { const [rows, setRows] = useState([]); const [rowModesModel, setRowModesModel] = useState({}); + const [isDeleteConfirmationOpen, setIsDeleteConfirmationOpen] = + useState(false); + const [deleteConfirmationId, setDeleteConfirmationId] = useState(null); useEffect(() => { if (data && data.subprojects.length > 0) { @@ -224,24 +162,45 @@ const SubprojectsTable = ({ projectId = null, refetchSummaryData }) => { [rowModesModel] ); - const handleRowModesModelChange = (newRowModesModel) => { - setRowModesModel(newRowModesModel); + const handleCancelClick = (id) => () => { + setRowModesModel({ + ...rowModesModel, + [id]: { mode: GridRowModes.View, ignoreModifications: true }, + }); + const editedRow = rows.find((row) => row.project_id === id); + if (editedRow.isNew) { + setRows(rows.filter((row) => row.id !== id)); + } }; - const processRowUpdate = (updatedRow) => { - // const updatedSubprojectData = updatedRow; + const handleDeleteOpen = useCallback((id) => { + setIsDeleteConfirmationOpen(true); + setDeleteConfirmationId(id); + }, []); - // delete updatedSubprojectData.isNew; - // delete updatedSubprojectData.id; + // handles row delete + const handleDeleteClick = useCallback( + (id) => () => { + const childProjectId = id; + return deleteProjectSubproject({ + variables: { + childProjectId: childProjectId, + }, + }) + .then(() => { + refetch(); + refetchSummaryData(); // Refresh subprojects in summary map + }) + .catch((error) => console.error(error)); + }, + [deleteProjectSubproject, refetch, refetchSummaryData] + ); - // updateProjectSubproject({ - // variables: { - // objects: { - // ...updatedSubprojectData, - // }, - // }, - // }); + const handleRowModesModelChange = (newRowModesModel) => { + setRowModesModel(newRowModesModel); + }; + const processRowUpdate = (updatedRow) => { const childProjectId = updatedRow?.project_name_full?.project_id; return updateProjectSubproject({ variables: { @@ -259,10 +218,9 @@ const SubprojectsTable = ({ projectId = null, refetchSummaryData }) => { const dataGridColumns = useColumns({ data, rowModesModel, - // handleDeleteOpen, + handleDeleteOpen, handleSaveClick, - // handleCancelClick, - // handleEditClick, + handleCancelClick, }); return ( @@ -279,97 +237,12 @@ const SubprojectsTable = ({ projectId = null, refetchSummaryData }) => { editMode="row" processRowUpdate={processRowUpdate} hideFooter - // data={data.subprojects ?? []} - // columns={columns} - // style={{ padding: "8px" }} - // components={{ - // // Note: in our other instances of Material Table, we bypass submitting the form on enter - // // In this table, since we currently only have one field to select, enter will submit the form - // Action: (props) => { - // // If isn't the add action - // if ( - // typeof props.action === typeof Function || - // props.action.tooltip !== "Add" - // ) { - // // return ; - // } else { - // return ( - // - // ); - // } - // }, - // Toolbar: (props) => ( - // // to have it align with table content - //
- // {/* */} - //
- // ), - // }} - // title={ - // - // Subprojects - // - // } - // options={{ - // paging: false, - // search: false, - // rowStyle: { fontFamily: typography.fontFamily }, - // actionsColumnIndex: -1, - // tableLayout: "fixed", - // addRowPosition: "first", - // idSynonym: "project_id", - // }} - // localization={{ - // header: { - // actions: "", - // }, - // body: { - // emptyDataSourceMessage: ( - // No subprojects to display - // ), - // editRow: { - // deleteText: "Are you sure you want to remove this subproject?", - // }, - // }, - // }} - // icons={{ Delete: DeleteOutlineIcon, Edit: EditOutlinedIcon }} - // editable={{ - // onRowAdd: (newData) => { - // const childProjectId = newData?.project_name_full?.project_id; - // return updateProjectSubproject({ - // variables: { - // parentProjectId: projectId, - // childProjectId: childProjectId, - // }, - // }) - // .then(() => { - // refetch(); - // refetchSummaryData(); // Refresh subprojects in summary map - // }) - // .catch((error) => console.error(error)); - // }, - // onRowDelete: (newData) => { - // const childProjectId = newData?.project_id; - // return deleteProjectSubproject({ - // variables: { - // childProjectId: childProjectId, - // }, - // }) - // .then(() => { - // refetch(); - // refetchSummaryData(); // Refresh subprojects in summary map - // }) - // .catch((error) => console.error(error)); - // }, - // }} + /> +
); From f7cd58d422016fa4ccfef25e12369b95e28caf83 Mon Sep 17 00:00:00 2001 From: rose Date: Mon, 11 Nov 2024 22:39:44 -0600 Subject: [PATCH 15/85] fix delete confirmation, add no rows label, make status column empty when editing --- .../ProjectSummary/SubprojectsTable.js | 39 ++++++++++--------- 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectsTable.js b/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectsTable.js index af60935607..16c5ff0778 100644 --- a/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectsTable.js +++ b/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectsTable.js @@ -32,7 +32,6 @@ const useColumns = ({ handleCancelClick, }) => useMemo(() => { - console.log(data); return [ { headerName: "ID", @@ -52,7 +51,7 @@ const useColumns = ({ { headerName: "Status", field: "status", - editable: false, + editable: true, width: 200, renderCell: ({ row }) => ( ), + renderEditCell: () =>
, }, { headerName: "", @@ -135,7 +135,6 @@ const SubprojectsTable = ({ projectId = null, refetchSummaryData }) => { if (loading || !data); const handleAddSubprojectClick = () => { - console.log("subproject clicked"); // use a random id to keep track of row in row modes model and data grid rows // before the record is added to the db const id = uuidv4(); @@ -179,22 +178,23 @@ const SubprojectsTable = ({ projectId = null, refetchSummaryData }) => { }, []); // handles row delete - const handleDeleteClick = useCallback( - (id) => () => { - const childProjectId = id; - return deleteProjectSubproject({ - variables: { - childProjectId: childProjectId, - }, + const handleDeleteClick = (id) => () => { + const childProjectId = id; + // remove row from rows in state + setRows(rows.filter((row) => row.project_id !== id)); + + deleteProjectSubproject({ + variables: { + childProjectId: childProjectId, + }, + }) + .then(() => { + refetch(); + refetchSummaryData(); // Refresh subprojects in summary map }) - .then(() => { - refetch(); - refetchSummaryData(); // Refresh subprojects in summary map - }) - .catch((error) => console.error(error)); - }, - [deleteProjectSubproject, refetch, refetchSummaryData] - ); + .then(() => setIsDeleteConfirmationOpen(false)) + .catch((error) => console.error(error)); + }; const handleRowModesModelChange = (newRowModesModel) => { setRowModesModel(newRowModesModel); @@ -229,6 +229,7 @@ const SubprojectsTable = ({ projectId = null, refetchSummaryData }) => { sx={dataGridProStyleOverrides} columns={dataGridColumns} rows={rows} + autoHeight getRowId={(row) => row.project_id} rowModesModel={rowModesModel} onRowModesModelChange={handleRowModesModelChange} @@ -237,6 +238,8 @@ const SubprojectsTable = ({ projectId = null, refetchSummaryData }) => { editMode="row" processRowUpdate={processRowUpdate} hideFooter + disableRowSelectionOnClick + localeText={{ noRowsLabel: "No subprojects to display" }} /> Date: Mon, 11 Nov 2024 22:50:57 -0600 Subject: [PATCH 16/85] check if is in edit mode to determine how to rendercell of status col --- .../ProjectSummary/SubprojectsTable.js | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectsTable.js b/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectsTable.js index 16c5ff0778..bc0bf982fa 100644 --- a/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectsTable.js +++ b/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectsTable.js @@ -51,16 +51,20 @@ const useColumns = ({ { headerName: "Status", field: "status", - editable: true, + editable: false, width: 200, - renderCell: ({ row }) => ( - - ), - renderEditCell: () =>
, + renderCell: ({ row, id }) => { + const isInEditMode = rowModesModel[id]?.mode === GridRowModes.Edit; + return [ + !isInEditMode && ( + + ), + ]; + }, }, { headerName: "", From 7478f66cf711616eccd17892def4859539ef229d Mon Sep 17 00:00:00 2001 From: rose Date: Fri, 15 Nov 2024 12:20:40 -0600 Subject: [PATCH 17/85] linting --- .../ProjectMilestones/MilestoneAutocompleteComponent.js | 1 - 1 file changed, 1 deletion(-) diff --git a/moped-editor/src/views/projects/projectView/ProjectMilestones/MilestoneAutocompleteComponent.js b/moped-editor/src/views/projects/projectView/ProjectMilestones/MilestoneAutocompleteComponent.js index 26e09f3c0c..0a7868e34e 100644 --- a/moped-editor/src/views/projects/projectView/ProjectMilestones/MilestoneAutocompleteComponent.js +++ b/moped-editor/src/views/projects/projectView/ProjectMilestones/MilestoneAutocompleteComponent.js @@ -7,7 +7,6 @@ import { } from "@mui/material"; import { useGridApiContext } from "@mui/x-data-grid-pro"; - /** * @param {Integer} id - Data Grid row id (same as record id) * @param {String} value - field value From dfb835e74eae0ca26b0f4dac0b7089e9443f234f Mon Sep 17 00:00:00 2001 From: rose Date: Fri, 15 Nov 2024 12:21:02 -0600 Subject: [PATCH 18/85] comment out this console log for now --- .../projectView/ProjectSummary/SubprojectLookupComponent.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectLookupComponent.js b/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectLookupComponent.js index 9523f59c40..a53f362534 100644 --- a/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectLookupComponent.js +++ b/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectLookupComponent.js @@ -5,9 +5,7 @@ import Autocomplete from "@mui/material/Autocomplete"; const SubprojectLookupComponent = ({ id, value, field, hasFocus, data }) => { const apiRef = useGridApiContext(); - const ref = React.useRef(null); - - console.log(hasFocus, ref, ref.current, "Test"); + // const ref = React.useRef(null); // React.useEffect(() => { // if (hasFocus) { From 80018bf2c7b659666c131472620004d61aedbfbd Mon Sep 17 00:00:00 2001 From: rose Date: Fri, 15 Nov 2024 12:21:34 -0600 Subject: [PATCH 19/85] dont render id when in edit mode, and other things --- .../ProjectSummary/SubprojectsTable.js | 36 ++++++++++++------- 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectsTable.js b/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectsTable.js index bc0bf982fa..5c3414f0ce 100644 --- a/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectsTable.js +++ b/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectsTable.js @@ -3,6 +3,7 @@ import { useQuery, useMutation } from "@apollo/client"; import CheckIcon from "@mui/icons-material/Check"; import CloseIcon from "@mui/icons-material/Close"; +import { CircularProgress } from "@mui/material"; import { DeleteOutline as DeleteOutlineIcon } from "@mui/icons-material"; import { DataGridPro, @@ -38,6 +39,10 @@ const useColumns = ({ field: "project_id", editable: false, width: 75, + renderCell: ({ id, value }) => { + const isInEditMode = rowModesModel[id]?.mode === GridRowModes.Edit; + return [!isInEditMode &&
{value}
]; // don't render anything if we are in edit mode + }, }, { headerName: "Full name", @@ -56,7 +61,7 @@ const useColumns = ({ renderCell: ({ row, id }) => { const isInEditMode = rowModesModel[id]?.mode === GridRowModes.Edit; return [ - !isInEditMode && ( + !isInEditMode && ( //don't render anything if we are in edit mode { const [updateProjectSubproject] = useMutation(UPDATE_PROJECT_SUBPROJECT); const [deleteProjectSubproject] = useMutation(DELETE_PROJECT_SUBPROJECT); + // rows and rowModesModel used in DataGrid const [rows, setRows] = useState([]); const [rowModesModel, setRowModesModel] = useState({}); const [isDeleteConfirmationOpen, setIsDeleteConfirmationOpen] = @@ -136,7 +142,6 @@ const SubprojectsTable = ({ projectId = null, refetchSummaryData }) => { }, [data]); if (error) console.error(error); - if (loading || !data); const handleAddSubprojectClick = () => { // use a random id to keep track of row in row modes model and data grid rows @@ -206,17 +211,22 @@ const SubprojectsTable = ({ projectId = null, refetchSummaryData }) => { const processRowUpdate = (updatedRow) => { const childProjectId = updatedRow?.project_name_full?.project_id; - return updateProjectSubproject({ - variables: { - parentProjectId: projectId, - childProjectId: childProjectId, - }, - }) - .then(() => { - refetch(); - refetchSummaryData(); // Refresh subprojects in summary map + return ( + updateProjectSubproject({ + variables: { + parentProjectId: projectId, + childProjectId: childProjectId, + }, }) - .catch((error) => console.error(error)); + .then(() => { + refetch(); + refetchSummaryData(); // Refresh subprojects in summary map + }) + // from the data grid docs: + // Please note that the processRowUpdate must return the row object to update the Data Grid internal state. + .then(() => updatedRow) + .catch((error) => console.error(error)) + ); }; const dataGridColumns = useColumns({ @@ -227,6 +237,8 @@ const SubprojectsTable = ({ projectId = null, refetchSummaryData }) => { handleCancelClick, }); + if (loading || !data) return ; + return ( Date: Fri, 15 Nov 2024 13:13:42 -0600 Subject: [PATCH 20/85] disable row editing, update some comments --- .../projectView/ProjectSummary/SubprojectsTable.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectsTable.js b/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectsTable.js index 5c3414f0ce..0530081760 100644 --- a/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectsTable.js +++ b/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectsTable.js @@ -41,7 +41,8 @@ const useColumns = ({ width: 75, renderCell: ({ id, value }) => { const isInEditMode = rowModesModel[id]?.mode === GridRowModes.Edit; - return [!isInEditMode &&
{value}
]; // don't render anything if we are in edit mode + // don't render anything if we are in edit mode because it will display the temp id + return [!isInEditMode &&
{value}
]; }, }, { @@ -61,7 +62,8 @@ const useColumns = ({ renderCell: ({ row, id }) => { const isInEditMode = rowModesModel[id]?.mode === GridRowModes.Edit; return [ - !isInEditMode && ( //don't render anything if we are in edit mode + // don't render anything if we are in edit mode + !isInEditMode && ( { hideFooter disableRowSelectionOnClick localeText={{ noRowsLabel: "No subprojects to display" }} + onRowEditStart={(event) => { + event.defaultMuiPrevented = true; // disable editing rows + }} /> Date: Fri, 15 Nov 2024 15:28:32 -0600 Subject: [PATCH 21/85] fix values flashing on screen on save click --- .../ProjectSummary/SubprojectsTable.js | 35 +++++++++---------- 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectsTable.js b/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectsTable.js index 0530081760..ec872ca444 100644 --- a/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectsTable.js +++ b/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectsTable.js @@ -39,11 +39,9 @@ const useColumns = ({ field: "project_id", editable: false, width: 75, - renderCell: ({ id, value }) => { - const isInEditMode = rowModesModel[id]?.mode === GridRowModes.Edit; - // don't render anything if we are in edit mode because it will display the temp id - return [!isInEditMode &&
{value}
]; - }, + renderCell: ({ row }) => + // prevents temp id from rendering when in edit mode + row.project_name_full && row.project_id, }, { headerName: "Full name", @@ -59,19 +57,15 @@ const useColumns = ({ field: "status", editable: false, width: 200, - renderCell: ({ row, id }) => { - const isInEditMode = rowModesModel[id]?.mode === GridRowModes.Edit; - return [ - // don't render anything if we are in edit mode - !isInEditMode && ( - - ), - ]; - }, + renderCell: ({ row }) => + // only render a badge once we exit edit mode and there is a phase + row.moped_proj_phases && ( + + ), }, { headerName: "", @@ -213,6 +207,11 @@ const SubprojectsTable = ({ projectId = null, refetchSummaryData }) => { const processRowUpdate = (updatedRow) => { const childProjectId = updatedRow?.project_name_full?.project_id; + + delete updatedRow.isNew; + updatedRow.id = null; + updatedRow.project_id = null; + return ( updateProjectSubproject({ variables: { From 55c55a796ad7426def95e93b4059e3139c97736e Mon Sep 17 00:00:00 2001 From: rose Date: Fri, 15 Nov 2024 15:36:51 -0600 Subject: [PATCH 22/85] disable editing rows --- .../projects/projectView/ProjectSummary/SubprojectsTable.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectsTable.js b/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectsTable.js index ec872ca444..523d94e7ce 100644 --- a/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectsTable.js +++ b/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectsTable.js @@ -257,7 +257,7 @@ const SubprojectsTable = ({ projectId = null, refetchSummaryData }) => { hideFooter disableRowSelectionOnClick localeText={{ noRowsLabel: "No subprojects to display" }} - onRowEditStart={(event) => { + onRowEditStart={(params, event) => { event.defaultMuiPrevented = true; // disable editing rows }} /> From 39fac2462ac815ae8d5a1acb00fd0dffa23718ee Mon Sep 17 00:00:00 2001 From: rose Date: Mon, 18 Nov 2024 14:59:20 -0600 Subject: [PATCH 23/85] allow sorting for status badge column by using valuegetter --- .../projects/projectView/ProjectSummary/SubprojectsTable.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectsTable.js b/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectsTable.js index 523d94e7ce..888d1f0ee4 100644 --- a/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectsTable.js +++ b/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectsTable.js @@ -54,9 +54,11 @@ const useColumns = ({ }, { headerName: "Status", - field: "status", + field: "moped_proj_phases", editable: false, width: 200, + // valueGetter allows us to derive from a nested field which will be used for sorting/filtering + valueGetter: (value) => value?.[0]?.moped_phase?.phase_name, renderCell: ({ row }) => // only render a badge once we exit edit mode and there is a phase row.moped_proj_phases && ( From 175e779ddef40ae6546fb760a22d88b076b20094 Mon Sep 17 00:00:00 2001 From: rose Date: Mon, 18 Nov 2024 15:29:28 -0600 Subject: [PATCH 24/85] fix unknown not sorting correctly --- .../projects/projectView/ProjectSummary/SubprojectsTable.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectsTable.js b/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectsTable.js index 888d1f0ee4..5540aaac6b 100644 --- a/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectsTable.js +++ b/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectsTable.js @@ -58,7 +58,8 @@ const useColumns = ({ editable: false, width: 200, // valueGetter allows us to derive from a nested field which will be used for sorting/filtering - valueGetter: (value) => value?.[0]?.moped_phase?.phase_name, + valueGetter: (value) => + value?.[0]?.moped_phase?.phase_name || "Unknown", renderCell: ({ row }) => // only render a badge once we exit edit mode and there is a phase row.moped_proj_phases && ( From e14dc5010680359b50d013a5a84315995e4fdc16 Mon Sep 17 00:00:00 2001 From: rose Date: Mon, 18 Nov 2024 15:36:00 -0600 Subject: [PATCH 25/85] this is finnicky --- .../projects/projectView/ProjectSummary/SubprojectsTable.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectsTable.js b/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectsTable.js index 5540aaac6b..609ca7af9a 100644 --- a/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectsTable.js +++ b/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectsTable.js @@ -62,12 +62,14 @@ const useColumns = ({ value?.[0]?.moped_phase?.phase_name || "Unknown", renderCell: ({ row }) => // only render a badge once we exit edit mode and there is a phase - row.moped_proj_phases && ( + row.moped_proj_phases ? ( + ) : ( +
), }, { From 3f25003b963b18e65c0ff5052ef633327df15e2e Mon Sep 17 00:00:00 2001 From: rose Date: Mon, 18 Nov 2024 16:55:54 -0600 Subject: [PATCH 26/85] fix deleteconfiromatin modal text --- .../projects/projectView/ProjectSummary/SubprojectsTable.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectsTable.js b/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectsTable.js index 609ca7af9a..9c279f2238 100644 --- a/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectsTable.js +++ b/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectsTable.js @@ -267,7 +267,7 @@ const SubprojectsTable = ({ projectId = null, refetchSummaryData }) => { }} /> Date: Mon, 18 Nov 2024 16:56:08 -0600 Subject: [PATCH 27/85] whyy isnt this working --- .../SubprojectLookupComponent.js | 22 +++++++++++++------ 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectLookupComponent.js b/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectLookupComponent.js index a53f362534..0a2370f564 100644 --- a/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectLookupComponent.js +++ b/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectLookupComponent.js @@ -3,16 +3,24 @@ import { useGridApiContext } from "@mui/x-data-grid-pro"; import { FormControl, FormHelperText, TextField } from "@mui/material"; import Autocomplete from "@mui/material/Autocomplete"; +/** Component for subproject lookup dropdown + * @param {Integer} id - Data Grid row id (same as project id) + * @param {String} value - field value + * @param {String} field - name of field + * @param {Boolean} hasFocus - does this field have focus + * @param {Object} data - data object with subproject options + * @return {JSX.Element} + */ + const SubprojectLookupComponent = ({ id, value, field, hasFocus, data }) => { const apiRef = useGridApiContext(); - // const ref = React.useRef(null); + const ref = React.useRef(null); - // React.useEffect(() => { - // if (hasFocus) { - // ref.current.focus(); - // console.log("we are focusing"); - // } - // }, [hasFocus]); + React.useEffect(() => { + if (hasFocus) { + ref.current.focus(); + } + }, [hasFocus]); const handleChange = (event, newValue) => { apiRef.current.setEditCellValue({ From ceb2f227f006f48df7bed3369a39b735dc1af331 Mon Sep 17 00:00:00 2001 From: rose Date: Tue, 19 Nov 2024 14:19:38 -0600 Subject: [PATCH 28/85] ha needed to pass the ref as a param obviouslyyy --- .../ProjectSummary/SubprojectLookupComponent.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectLookupComponent.js b/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectLookupComponent.js index 0a2370f564..b866fa4953 100644 --- a/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectLookupComponent.js +++ b/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectLookupComponent.js @@ -1,4 +1,4 @@ -import React from "react"; +import React, { useEffect, useRef } from "react"; import { useGridApiContext } from "@mui/x-data-grid-pro"; import { FormControl, FormHelperText, TextField } from "@mui/material"; import Autocomplete from "@mui/material/Autocomplete"; @@ -14,9 +14,9 @@ import Autocomplete from "@mui/material/Autocomplete"; const SubprojectLookupComponent = ({ id, value, field, hasFocus, data }) => { const apiRef = useGridApiContext(); - const ref = React.useRef(null); + const ref = useRef(null); - React.useEffect(() => { + useEffect(() => { if (hasFocus) { ref.current.focus(); } @@ -41,7 +41,9 @@ const SubprojectLookupComponent = ({ id, value, field, hasFocus, data }) => { } value={value || null} onChange={handleChange} - renderInput={(params) => } + renderInput={(params) => ( + + )} /> Required From 90448052a596bfa5241292506bb9b4c4f9e15d08 Mon Sep 17 00:00:00 2001 From: rose Date: Tue, 19 Nov 2024 17:38:29 -0600 Subject: [PATCH 29/85] add comments, more usecallbacks, fix datagrid error about not having an id prop on processrowudate bc i was deleting the id pre mutation --- .../ProjectSummary/SubprojectsTable.js | 91 +++++++++++-------- 1 file changed, 55 insertions(+), 36 deletions(-) diff --git a/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectsTable.js b/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectsTable.js index 9c279f2238..7d59dd1842 100644 --- a/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectsTable.js +++ b/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectsTable.js @@ -10,8 +10,9 @@ import { GridRowModes, GridActionsCellItem, } from "@mui/x-data-grid-pro"; -import dataGridProStyleOverrides from "src/styles/dataGridProStylesOverrides"; import { v4 as uuidv4 } from "uuid"; + +import dataGridProStyleOverrides from "src/styles/dataGridProStylesOverrides"; import SubprojectsToolbar from "./SubprojectsToolbar"; import ApolloErrorHandler from "../../../../components/ApolloErrorHandler"; import ProjectStatusBadge from "../../projectView/ProjectStatusBadge"; @@ -136,22 +137,27 @@ const SubprojectsTable = ({ projectId = null, refetchSummaryData }) => { useState(false); const [deleteConfirmationId, setDeleteConfirmationId] = useState(null); + // sets the data grid row data when query data is fetched useEffect(() => { if (data && data.subprojects.length > 0) { - setRows(data.subprojects); + const rowsWithId = data.subprojects.map((row) => { + return { ...row, id: row.project_id }; + }); + setRows(rowsWithId); } }, [data]); if (error) console.error(error); + // adds a blank row to the table and updates the row modes model const handleAddSubprojectClick = () => { // use a random id to keep track of row in row modes model and data grid rows // before the record is added to the db const id = uuidv4(); setRows((oldRows) => [ { - id, - project_id: id, + id: id, + project_id: null, project_name_full: null, status: null, isNew: true, @@ -164,6 +170,7 @@ const SubprojectsTable = ({ projectId = null, refetchSummaryData }) => { })); }; + // handles saving the new row by clicking the check icon const handleSaveClick = useCallback( (id) => () => { setRowModesModel({ ...rowModesModel, [id]: { mode: GridRowModes.View } }); @@ -171,17 +178,22 @@ const SubprojectsTable = ({ projectId = null, refetchSummaryData }) => { [rowModesModel] ); - const handleCancelClick = (id) => () => { - setRowModesModel({ - ...rowModesModel, - [id]: { mode: GridRowModes.View, ignoreModifications: true }, - }); - const editedRow = rows.find((row) => row.project_id === id); - if (editedRow.isNew) { - setRows(rows.filter((row) => row.id !== id)); - } - }; + // handles canceling adding a new row by clicking the X icon + const handleCancelClick = useCallback( + (id) => () => { + setRowModesModel({ + ...rowModesModel, + [id]: { mode: GridRowModes.View, ignoreModifications: true }, + }); + const editedRow = rows.find((row) => row.id === id); + if (editedRow.isNew) { + setRows(rows.filter((row) => row.id !== id)); + } + }, + [rowModesModel, rows] + ); + // open the delete confirmation modal const handleDeleteOpen = useCallback((id) => { setIsDeleteConfirmationOpen(true); setDeleteConfirmationId(id); @@ -210,30 +222,36 @@ const SubprojectsTable = ({ projectId = null, refetchSummaryData }) => { setRowModesModel(newRowModesModel); }; - const processRowUpdate = (updatedRow) => { - const childProjectId = updatedRow?.project_name_full?.project_id; + const handleProcessUpdateError = (error) => { + console.error(error.message); + }; - delete updatedRow.isNew; - updatedRow.id = null; - updatedRow.project_id = null; + // handles insert mutation triggered by row mode switching from edit to view + const processRowUpdate = useCallback( + (updatedRow) => { + const childProjectId = updatedRow?.project_name_full?.project_id; - return ( - updateProjectSubproject({ - variables: { - parentProjectId: projectId, - childProjectId: childProjectId, - }, - }) - .then(() => { - refetch(); - refetchSummaryData(); // Refresh subprojects in summary map + delete updatedRow.isNew; + + return ( + updateProjectSubproject({ + variables: { + parentProjectId: projectId, + childProjectId: childProjectId, + }, }) - // from the data grid docs: - // Please note that the processRowUpdate must return the row object to update the Data Grid internal state. - .then(() => updatedRow) - .catch((error) => console.error(error)) - ); - }; + .then(() => { + refetch(); + refetchSummaryData(); // Refresh subprojects in summary map + }) + // from the data grid docs: + // Please note that the processRowUpdate must return the row object to update the Data Grid internal state. + .then(() => updatedRow) + .catch((error) => console.error(error)) + ); + }, + [projectId, refetch, refetchSummaryData, updateProjectSubproject] + ); const dataGridColumns = useColumns({ data, @@ -252,13 +270,14 @@ const SubprojectsTable = ({ projectId = null, refetchSummaryData }) => { columns={dataGridColumns} rows={rows} autoHeight - getRowId={(row) => row.project_id} + getRowId={(row) => row.id} rowModesModel={rowModesModel} onRowModesModelChange={handleRowModesModelChange} slots={{ toolbar: SubprojectsToolbar }} slotProps={{ toolbar: { onClick: handleAddSubprojectClick } }} editMode="row" processRowUpdate={processRowUpdate} + onProcessRowUpdateError={handleProcessUpdateError} hideFooter disableRowSelectionOnClick localeText={{ noRowsLabel: "No subprojects to display" }} From 8b8f6110af33277d927e1dc4af4c7a305be7ea67 Mon Sep 17 00:00:00 2001 From: rose Date: Wed, 20 Nov 2024 14:30:33 -0600 Subject: [PATCH 30/85] revert some of those updates --- .../ProjectSummary/SubprojectsTable.js | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectsTable.js b/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectsTable.js index 7d59dd1842..70622d4ff5 100644 --- a/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectsTable.js +++ b/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectsTable.js @@ -140,15 +140,17 @@ const SubprojectsTable = ({ projectId = null, refetchSummaryData }) => { // sets the data grid row data when query data is fetched useEffect(() => { if (data && data.subprojects.length > 0) { - const rowsWithId = data.subprojects.map((row) => { - return { ...row, id: row.project_id }; - }); - setRows(rowsWithId); + // const rowsWithId = data.subprojects.map((row) => { + // return { ...row, id: row.project_id }; + // }); + setRows(data.subprojects); } }, [data]); if (error) console.error(error); + console.log(rows, "rows"); + // adds a blank row to the table and updates the row modes model const handleAddSubprojectClick = () => { // use a random id to keep track of row in row modes model and data grid rows @@ -156,8 +158,8 @@ const SubprojectsTable = ({ projectId = null, refetchSummaryData }) => { const id = uuidv4(); setRows((oldRows) => [ { - id: id, - project_id: null, + id, + project_id: id, project_name_full: null, status: null, isNew: true, @@ -232,6 +234,8 @@ const SubprojectsTable = ({ projectId = null, refetchSummaryData }) => { const childProjectId = updatedRow?.project_name_full?.project_id; delete updatedRow.isNew; + // updatedRow.id = null; + // updatedRow.project_id = null; return ( updateProjectSubproject({ @@ -270,7 +274,7 @@ const SubprojectsTable = ({ projectId = null, refetchSummaryData }) => { columns={dataGridColumns} rows={rows} autoHeight - getRowId={(row) => row.id} + getRowId={(row) => row.project_id} rowModesModel={rowModesModel} onRowModesModelChange={handleRowModesModelChange} slots={{ toolbar: SubprojectsToolbar }} From ac53aef547bac27014017a571519474dbaab428d Mon Sep 17 00:00:00 2001 From: rose Date: Wed, 20 Nov 2024 17:16:35 -0600 Subject: [PATCH 31/85] delete project name in process row update, now no flashing values but just a blank row for a sec? --- .../ProjectSummary/SubprojectsTable.js | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectsTable.js b/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectsTable.js index 70622d4ff5..8abb97ab13 100644 --- a/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectsTable.js +++ b/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectsTable.js @@ -140,10 +140,10 @@ const SubprojectsTable = ({ projectId = null, refetchSummaryData }) => { // sets the data grid row data when query data is fetched useEffect(() => { if (data && data.subprojects.length > 0) { - // const rowsWithId = data.subprojects.map((row) => { - // return { ...row, id: row.project_id }; - // }); - setRows(data.subprojects); + const rowsWithId = data.subprojects.map((row) => { + return { ...row, id: row.project_id }; + }); + setRows(rowsWithId); } }, [data]); @@ -158,8 +158,8 @@ const SubprojectsTable = ({ projectId = null, refetchSummaryData }) => { const id = uuidv4(); setRows((oldRows) => [ { - id, - project_id: id, + id: id, + project_id: null, project_name_full: null, status: null, isNew: true, @@ -233,9 +233,10 @@ const SubprojectsTable = ({ projectId = null, refetchSummaryData }) => { (updatedRow) => { const childProjectId = updatedRow?.project_name_full?.project_id; - delete updatedRow.isNew; + // delete updatedRow.isNew; // updatedRow.id = null; // updatedRow.project_id = null; + updatedRow.project_name_full = null; return ( updateProjectSubproject({ @@ -274,7 +275,7 @@ const SubprojectsTable = ({ projectId = null, refetchSummaryData }) => { columns={dataGridColumns} rows={rows} autoHeight - getRowId={(row) => row.project_id} + getRowId={(row) => row.id} rowModesModel={rowModesModel} onRowModesModelChange={handleRowModesModelChange} slots={{ toolbar: SubprojectsToolbar }} From 1586262ca79175d1740f9e1e4c5086d43f844637 Mon Sep 17 00:00:00 2001 From: rose Date: Thu, 21 Nov 2024 11:29:41 -0600 Subject: [PATCH 32/85] update comments and switch order of refetch to prevent blank row from showing for a second after save click --- .../projectView/ProjectSummary/SubprojectsTable.js | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectsTable.js b/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectsTable.js index 8abb97ab13..d99fd3fc56 100644 --- a/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectsTable.js +++ b/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectsTable.js @@ -140,6 +140,8 @@ const SubprojectsTable = ({ projectId = null, refetchSummaryData }) => { // sets the data grid row data when query data is fetched useEffect(() => { if (data && data.subprojects.length > 0) { + // because we actually render the project_id in the table we run into issues with using + // it as the datagrid row id, so we want to make a separate id value to do that const rowsWithId = data.subprojects.map((row) => { return { ...row, id: row.project_id }; }); @@ -149,8 +151,6 @@ const SubprojectsTable = ({ projectId = null, refetchSummaryData }) => { if (error) console.error(error); - console.log(rows, "rows"); - // adds a blank row to the table and updates the row modes model const handleAddSubprojectClick = () => { // use a random id to keep track of row in row modes model and data grid rows @@ -233,9 +233,6 @@ const SubprojectsTable = ({ projectId = null, refetchSummaryData }) => { (updatedRow) => { const childProjectId = updatedRow?.project_name_full?.project_id; - // delete updatedRow.isNew; - // updatedRow.id = null; - // updatedRow.project_id = null; updatedRow.project_name_full = null; return ( @@ -245,13 +242,13 @@ const SubprojectsTable = ({ projectId = null, refetchSummaryData }) => { childProjectId: childProjectId, }, }) + // from the data grid docs: + // Please note that the processRowUpdate must return the row object to update the Data Grid internal state. + .then(() => updatedRow) .then(() => { refetch(); refetchSummaryData(); // Refresh subprojects in summary map }) - // from the data grid docs: - // Please note that the processRowUpdate must return the row object to update the Data Grid internal state. - .then(() => updatedRow) .catch((error) => console.error(error)) ); }, From c7074639959386c5c9147b66cfd895c623442962 Mon Sep 17 00:00:00 2001 From: rose Date: Thu, 21 Nov 2024 13:37:41 -0600 Subject: [PATCH 33/85] return phases in update query --- moped-editor/src/queries/subprojects.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/moped-editor/src/queries/subprojects.js b/moped-editor/src/queries/subprojects.js index 21e04e3ce9..2ced1e2d47 100644 --- a/moped-editor/src/queries/subprojects.js +++ b/moped-editor/src/queries/subprojects.js @@ -31,6 +31,12 @@ export const SUBPROJECT_QUERY = gql` ) { project_id project_name_full + moped_proj_phases(where: { is_current_phase: { _eq: true } }) { + moped_phase { + phase_name + phase_key + } + } } } `; From 99adfa83854fc4e896f20ac6e5446b9cc5f64d6a Mon Sep 17 00:00:00 2001 From: rose Date: Thu, 21 Nov 2024 13:39:22 -0600 Subject: [PATCH 34/85] add comment --- .../projectView/ProjectSummary/SubprojectLookupComponent.js | 1 + 1 file changed, 1 insertion(+) diff --git a/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectLookupComponent.js b/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectLookupComponent.js index b866fa4953..60bc6b9b18 100644 --- a/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectLookupComponent.js +++ b/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectLookupComponent.js @@ -26,6 +26,7 @@ const SubprojectLookupComponent = ({ id, value, field, hasFocus, data }) => { apiRef.current.setEditCellValue({ id, field, + // this will be an object containing the project id, name, and status for the project selected value: newValue ? newValue : null, }); }; From 45443bed476dc7ac7355f38d4ac9a07eed77e8b2 Mon Sep 17 00:00:00 2001 From: rose Date: Thu, 21 Nov 2024 13:40:13 -0600 Subject: [PATCH 35/85] remove unecessary render cell, fix blank row flashing after save because we were setting newRow with empty vals, need to manually set them bc we have a weird use case --- .../ProjectSummary/SubprojectsTable.js | 25 +++++++++---------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectsTable.js b/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectsTable.js index d99fd3fc56..ab561e2f60 100644 --- a/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectsTable.js +++ b/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectsTable.js @@ -40,9 +40,6 @@ const useColumns = ({ field: "project_id", editable: false, width: 75, - renderCell: ({ row }) => - // prevents temp id from rendering when in edit mode - row.project_name_full && row.project_id, }, { headerName: "Full name", @@ -141,7 +138,7 @@ const SubprojectsTable = ({ projectId = null, refetchSummaryData }) => { useEffect(() => { if (data && data.subprojects.length > 0) { // because we actually render the project_id in the table we run into issues with using - // it as the datagrid row id, so we want to make a separate id value to do that + // it as the datagrid row id, so we want to make a separate id value for that const rowsWithId = data.subprojects.map((row) => { return { ...row, id: row.project_id }; }); @@ -149,8 +146,6 @@ const SubprojectsTable = ({ projectId = null, refetchSummaryData }) => { } }, [data]); - if (error) console.error(error); - // adds a blank row to the table and updates the row modes model const handleAddSubprojectClick = () => { // use a random id to keep track of row in row modes model and data grid rows @@ -230,10 +225,14 @@ const SubprojectsTable = ({ projectId = null, refetchSummaryData }) => { // handles insert mutation triggered by row mode switching from edit to view const processRowUpdate = useCallback( - (updatedRow) => { - const childProjectId = updatedRow?.project_name_full?.project_id; - - updatedRow.project_name_full = null; + (newRow) => { + const childProjectId = newRow?.project_name_full?.project_id; + // newRow.project_name_full is an object containing the id, name, and phase for the project selected by the + // lookup component, we will use it for setting all of the newRow fields needed to update the datagrid internal state + newRow.project_id = newRow.project_name_full.project_id; + newRow.moped_proj_phases = newRow.project_name_full.moped_proj_phases; + newRow.project_name_full = newRow.project_name_full.project_name_full; + newRow.isNew = false; return ( updateProjectSubproject({ @@ -242,13 +241,13 @@ const SubprojectsTable = ({ projectId = null, refetchSummaryData }) => { childProjectId: childProjectId, }, }) - // from the data grid docs: - // Please note that the processRowUpdate must return the row object to update the Data Grid internal state. - .then(() => updatedRow) .then(() => { refetch(); refetchSummaryData(); // Refresh subprojects in summary map }) + // from the data grid docs: + // Please note that the processRowUpdate must return the row object to update the Data Grid internal state. + .then(() => newRow) .catch((error) => console.error(error)) ); }, From a726efa3d029f0fdb68643baac3138230cd85827 Mon Sep 17 00:00:00 2001 From: rose Date: Thu, 21 Nov 2024 15:46:04 -0600 Subject: [PATCH 36/85] linting --- moped-editor/src/components/RenderFieldLink.js | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/moped-editor/src/components/RenderFieldLink.js b/moped-editor/src/components/RenderFieldLink.js index 5cbe7dd4cc..d32716f110 100644 --- a/moped-editor/src/components/RenderFieldLink.js +++ b/moped-editor/src/components/RenderFieldLink.js @@ -1,16 +1,13 @@ import React from "react"; import { NavLink as RouterLink } from "react-router-dom"; -import theme from "src/theme/index" +import theme from "src/theme/index"; const RenderFieldLink = ({ projectId, value, tab }) => { const route = tab ? `/moped/projects/${projectId}?tab=${tab}` : `/moped/projects/${projectId}/`; return ( - + {value} ); From 9b11ad980a49910127d082655a4746f84eb5b90e Mon Sep 17 00:00:00 2001 From: rose Date: Thu, 21 Nov 2024 15:46:22 -0600 Subject: [PATCH 37/85] link to the project --- .../projectView/ProjectSummary/SubprojectsTable.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectsTable.js b/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectsTable.js index ab561e2f60..2d398bae74 100644 --- a/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectsTable.js +++ b/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectsTable.js @@ -18,6 +18,7 @@ import ApolloErrorHandler from "../../../../components/ApolloErrorHandler"; import ProjectStatusBadge from "../../projectView/ProjectStatusBadge"; import SubprojectLookupComponent from "./SubprojectLookupComponent"; import DeleteConfirmationModal from "../DeleteConfirmationModal"; +import RenderFieldLink from "src/components/RenderFieldLink"; import { SUBPROJECT_QUERY, @@ -46,6 +47,12 @@ const useColumns = ({ field: "project_name_full", editable: true, width: 250, + renderCell: ({ row }) => ( + + ), renderEditCell: (props) => ( ), From 9c041a010d7600a9025a982701b6e4f7413bae5e Mon Sep 17 00:00:00 2001 From: rose Date: Fri, 22 Nov 2024 14:22:45 -0600 Subject: [PATCH 38/85] check if there is there is a newRow.project_name_full --- .../ProjectSummary/SubprojectsTable.js | 46 ++++++++++--------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectsTable.js b/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectsTable.js index 2d398bae74..2d5859f272 100644 --- a/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectsTable.js +++ b/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectsTable.js @@ -233,30 +233,32 @@ const SubprojectsTable = ({ projectId = null, refetchSummaryData }) => { // handles insert mutation triggered by row mode switching from edit to view const processRowUpdate = useCallback( (newRow) => { - const childProjectId = newRow?.project_name_full?.project_id; - // newRow.project_name_full is an object containing the id, name, and phase for the project selected by the - // lookup component, we will use it for setting all of the newRow fields needed to update the datagrid internal state - newRow.project_id = newRow.project_name_full.project_id; - newRow.moped_proj_phases = newRow.project_name_full.moped_proj_phases; - newRow.project_name_full = newRow.project_name_full.project_name_full; - newRow.isNew = false; + if (newRow.project_name_full) { + const childProjectId = newRow?.project_name_full?.project_id; + // newRow.project_name_full is an object containing the id, name, and phase for the project selected by the + // lookup component, we will use it for setting all of the newRow fields needed to update the datagrid internal state + newRow.project_id = newRow.project_name_full.project_id; + newRow.moped_proj_phases = newRow.project_name_full.moped_proj_phases; + newRow.project_name_full = newRow.project_name_full.project_name_full; + newRow.isNew = false; - return ( - updateProjectSubproject({ - variables: { - parentProjectId: projectId, - childProjectId: childProjectId, - }, - }) - .then(() => { - refetch(); - refetchSummaryData(); // Refresh subprojects in summary map + return ( + updateProjectSubproject({ + variables: { + parentProjectId: projectId, + childProjectId: childProjectId, + }, }) - // from the data grid docs: - // Please note that the processRowUpdate must return the row object to update the Data Grid internal state. - .then(() => newRow) - .catch((error) => console.error(error)) - ); + .then(() => { + refetch(); + refetchSummaryData(); // Refresh subprojects in summary map + }) + // from the data grid docs: + // Please note that the processRowUpdate must return the row object to update the Data Grid internal state. + .then(() => newRow) + .catch((error) => console.error(error)) + ); + } }, [projectId, refetch, refetchSummaryData, updateProjectSubproject] ); From 0621566429eab3348fbb911532156d5bf5b34a46 Mon Sep 17 00:00:00 2001 From: rose Date: Mon, 25 Nov 2024 15:38:33 -0600 Subject: [PATCH 39/85] add the clickaway listener --- .../src/components/GridTable/Search.js | 27 ++++++++++--------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/moped-editor/src/components/GridTable/Search.js b/moped-editor/src/components/GridTable/Search.js index 5fb535da5b..efa19d9fcd 100644 --- a/moped-editor/src/components/GridTable/Search.js +++ b/moped-editor/src/components/GridTable/Search.js @@ -7,6 +7,7 @@ import { Grid, Paper, Popper, + ClickAwayListener, } from "@mui/material"; import FormControlLabel from "@mui/material/FormControlLabel"; import FormGroup from "@mui/material/FormGroup"; @@ -244,18 +245,20 @@ const Search = ({ placement={"bottom"} className={classes.advancedSearchRoot} > - - - + + + + +
); From 6f82a23d2e5cd57d3353ef62eedba025e88dcc87 Mon Sep 17 00:00:00 2001 From: rose Date: Mon, 25 Nov 2024 15:38:42 -0600 Subject: [PATCH 40/85] change select to an autocomplete --- .../src/components/GridTable/Filters.js | 48 +++++++++++++++---- 1 file changed, 39 insertions(+), 9 deletions(-) diff --git a/moped-editor/src/components/GridTable/Filters.js b/moped-editor/src/components/GridTable/Filters.js index 05b03bf889..1dfb857aa2 100644 --- a/moped-editor/src/components/GridTable/Filters.js +++ b/moped-editor/src/components/GridTable/Filters.js @@ -126,6 +126,8 @@ const Filters = ({ */ const classes = useStyles(); + console.log(Object.values(filtersConfig), "values filter config"); + const { loading, error, data } = useQuery(LOOKUP_TABLES_QUERY); if (error) console.error(error); @@ -154,7 +156,12 @@ const Filters = ({ /* Some features like all/any radios require more than one filter to appear */ const areMoreThanOneFilters = filterParameters.length > 1; - const autocompleteOptionsMap = useCreateAutocompleteOptions(filtersConfig, data); + const autocompleteOptionsMap = useCreateAutocompleteOptions( + filtersConfig, + data + ); + + console.log(autocompleteOptionsMap, "auto complete options"); /** * Handles the click event on the field drop-down menu @@ -386,11 +393,11 @@ const Filters = ({ const { label, type } = fieldConfig ?? {}; const operators = fieldConfig?.operators ?? []; + console.log(operators, "operators"); + /* If the field uses a lookup table, get the table and field names */ - const { - table_name: lookupTable, - operators: lookupOperators, - } = fieldConfig?.lookup ?? {}; + const { table_name: lookupTable, operators: lookupOperators } = + fieldConfig?.lookup ?? {}; /* Check filter row validity */ const isValidInput = checkIsValidInput(filter, type); @@ -445,12 +452,12 @@ const Filters = ({ fullWidth className={classes.formControl} > - Operator - - ); })} - + */} + + operator ? FILTERS_COMMON_OPERATORS[operator]?.label : "" + } + onChange={(e) => + handleFilterOperatorChange( + filterIndex, + e.target.value, + lookupTable, + lookupOperators + ) + } + renderInput={(params) => ( + + )} + /> From 80fc358950b1d0e76e79786ca25ecbaa177cf39b Mon Sep 17 00:00:00 2001 From: rose Date: Mon, 25 Nov 2024 17:24:22 -0600 Subject: [PATCH 41/85] remove console logs, fix value and on change --- .../src/components/GridTable/Filters.js | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/moped-editor/src/components/GridTable/Filters.js b/moped-editor/src/components/GridTable/Filters.js index 1dfb857aa2..866514fbe1 100644 --- a/moped-editor/src/components/GridTable/Filters.js +++ b/moped-editor/src/components/GridTable/Filters.js @@ -5,10 +5,7 @@ import { useQuery } from "@apollo/client"; import { Button, TextField, - InputLabel, - Select, FormControl, - MenuItem, Grid, Hidden, Icon, @@ -126,8 +123,6 @@ const Filters = ({ */ const classes = useStyles(); - console.log(Object.values(filtersConfig), "values filter config"); - const { loading, error, data } = useQuery(LOOKUP_TABLES_QUERY); if (error) console.error(error); @@ -161,8 +156,6 @@ const Filters = ({ data ); - console.log(autocompleteOptionsMap, "auto complete options"); - /** * Handles the click event on the field drop-down menu * @param {string} filterIndex - filterParameters index to modify @@ -393,8 +386,6 @@ const Filters = ({ const { label, type } = fieldConfig ?? {}; const operators = fieldConfig?.operators ?? []; - console.log(operators, "operators"); - /* If the field uses a lookup table, get the table and field names */ const { table_name: lookupTable, operators: lookupOperators } = fieldConfig?.lookup ?? {}; @@ -490,16 +481,16 @@ const Filters = ({ })} */} - operator ? FILTERS_COMMON_OPERATORS[operator]?.label : "" + FILTERS_COMMON_OPERATORS[operator]?.label || "" } - onChange={(e) => + onChange={(e, value) => handleFilterOperatorChange( filterIndex, - e.target.value, + value, lookupTable, lookupOperators ) From 1ad49850ae05315133d1d0ddb6a8432625d12092 Mon Sep 17 00:00:00 2001 From: rose Date: Mon, 25 Nov 2024 17:30:28 -0600 Subject: [PATCH 42/85] remove this commented out part --- .../src/components/GridTable/Filters.js | 37 ------------------- 1 file changed, 37 deletions(-) diff --git a/moped-editor/src/components/GridTable/Filters.js b/moped-editor/src/components/GridTable/Filters.js index 866514fbe1..8dc426aeaa 100644 --- a/moped-editor/src/components/GridTable/Filters.js +++ b/moped-editor/src/components/GridTable/Filters.js @@ -443,43 +443,6 @@ const Filters = ({ fullWidth className={classes.formControl} > - {/* - Operator - */} - {/* */} Date: Mon, 25 Nov 2024 17:32:49 -0600 Subject: [PATCH 43/85] add back in this id prop --- moped-editor/src/components/GridTable/Filters.js | 1 + 1 file changed, 1 insertion(+) diff --git a/moped-editor/src/components/GridTable/Filters.js b/moped-editor/src/components/GridTable/Filters.js index 8dc426aeaa..1b7c22a5c9 100644 --- a/moped-editor/src/components/GridTable/Filters.js +++ b/moped-editor/src/components/GridTable/Filters.js @@ -445,6 +445,7 @@ const Filters = ({ > From e42260bec67badb9dccb42853b230c7b9adb9cf5 Mon Sep 17 00:00:00 2001 From: Mike Date: Tue, 26 Nov 2024 17:03:24 -0600 Subject: [PATCH 44/85] Add up/down migration for department soft deletes and add new department --- .../migrations/1732661952305_department_reorg/down.sql | 3 +++ .../migrations/1732661952305_department_reorg/up.sql | 5 +++++ 2 files changed, 8 insertions(+) create mode 100644 moped-database/migrations/1732661952305_department_reorg/down.sql create mode 100644 moped-database/migrations/1732661952305_department_reorg/up.sql diff --git a/moped-database/migrations/1732661952305_department_reorg/down.sql b/moped-database/migrations/1732661952305_department_reorg/down.sql new file mode 100644 index 0000000000..e583fe7837 --- /dev/null +++ b/moped-database/migrations/1732661952305_department_reorg/down.sql @@ -0,0 +1,3 @@ +-- Updating moped_department table will be up only. If we need to revert, we will need do it manually or +-- update with a future migration. +SELECT 0; diff --git a/moped-database/migrations/1732661952305_department_reorg/up.sql b/moped-database/migrations/1732661952305_department_reorg/up.sql new file mode 100644 index 0000000000..8ae88c02fa --- /dev/null +++ b/moped-database/migrations/1732661952305_department_reorg/up.sql @@ -0,0 +1,5 @@ +-- Add soft deletes to department table +ALTER TABLE moped_department ADD is_deleted boolean DEFAULT FALSE; + +INSERT INTO "public"."moped_department" ("department_name", "department_abbreviation", "organization_id") VALUES +('Austin Transportation and Public Works', 'TPW', 1); From 1d0f5e086a4988a62589a052976ffef186cf15c3 Mon Sep 17 00:00:00 2001 From: Mike Date: Tue, 26 Nov 2024 17:12:34 -0600 Subject: [PATCH 45/85] Soft delete ATD & PWD; update workgroup department ids to TPW id --- .../1732661952305_department_reorg/up.sql | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/moped-database/migrations/1732661952305_department_reorg/up.sql b/moped-database/migrations/1732661952305_department_reorg/up.sql index 8ae88c02fa..77c74f66df 100644 --- a/moped-database/migrations/1732661952305_department_reorg/up.sql +++ b/moped-database/migrations/1732661952305_department_reorg/up.sql @@ -3,3 +3,34 @@ ALTER TABLE moped_department ADD is_deleted boolean DEFAULT FALSE; INSERT INTO "public"."moped_department" ("department_name", "department_abbreviation", "organization_id") VALUES ('Austin Transportation and Public Works', 'TPW', 1); + +-- Soft delete existing department records that are merging into the new one +UPDATE moped_department SET is_deleted = TRUE WHERE department_name IN ('Austin Transportation', 'Public Works'); + +-- Find existing workgroup records that are associated with the departments that are merging +-- and update them to the new department row called Austin Transportation and Public Works +WITH department_todos AS ( + SELECT department_id AS ids + FROM + moped_department + WHERE + department_name IN ( + 'Austin Transportation', + 'Public Works' + ) +), + +new_department_row AS ( + SELECT department_id AS id + FROM + moped_department + WHERE + department_name = 'Austin Transportation and Public Works' +) + +UPDATE +moped_workgroup +SET + department_id = (SELECT id FROM new_department_row) +WHERE + department_id IN (SELECT ids FROM department_todos); From adca6c5a0e2b6449158711df69d1cd4472f76543 Mon Sep 17 00:00:00 2001 From: Mike Date: Tue, 26 Nov 2024 18:00:15 -0600 Subject: [PATCH 46/85] Update entity departments too and add missing fk constraint --- .../1732661952305_department_reorg/up.sql | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/moped-database/migrations/1732661952305_department_reorg/up.sql b/moped-database/migrations/1732661952305_department_reorg/up.sql index 77c74f66df..a77a94158b 100644 --- a/moped-database/migrations/1732661952305_department_reorg/up.sql +++ b/moped-database/migrations/1732661952305_department_reorg/up.sql @@ -1,5 +1,6 @@ -- Add soft deletes to department table ALTER TABLE moped_department ADD is_deleted boolean DEFAULT FALSE; +COMMENT ON COLUMN moped_department.is_deleted IS 'Indicates soft deletion'; INSERT INTO "public"."moped_department" ("department_name", "department_abbreviation", "organization_id") VALUES ('Austin Transportation and Public Works', 'TPW', 1); @@ -34,3 +35,36 @@ SET department_id = (SELECT id FROM new_department_row) WHERE department_id IN (SELECT ids FROM department_todos); + +-- Find existing entity records that are associated with the departments that are merging +-- and update them to the new department row called Austin Transportation and Public Works +WITH department_todos AS ( + SELECT department_id AS ids + FROM + moped_department + WHERE + department_name IN ( + 'Austin Transportation', + 'Public Works' + ) +), + +new_department_row AS ( + SELECT department_id AS id + FROM + moped_department + WHERE + department_name = 'Austin Transportation and Public Works' +) + +UPDATE +moped_entity +SET + department_id = (SELECT id FROM new_department_row) +WHERE + department_id IN (SELECT ids FROM department_todos); + +-- Add foreign key constraints to entity table department_id column; moped_workgroup already has this constraint +ALTER TABLE moped_entity +ADD CONSTRAINT moped_entity_department_id_fkey FOREIGN KEY ("department_id") +REFERENCES moped_department ("department_id"); From f316579a5d1ee670853a7c846a08609ab0e321dc Mon Sep 17 00:00:00 2001 From: Mike Date: Tue, 26 Nov 2024 18:16:05 -0600 Subject: [PATCH 47/85] Add other fk constraints --- .../1732661952305_department_reorg/up.sql | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/moped-database/migrations/1732661952305_department_reorg/up.sql b/moped-database/migrations/1732661952305_department_reorg/up.sql index a77a94158b..ef1d46a8b1 100644 --- a/moped-database/migrations/1732661952305_department_reorg/up.sql +++ b/moped-database/migrations/1732661952305_department_reorg/up.sql @@ -68,3 +68,17 @@ WHERE ALTER TABLE moped_entity ADD CONSTRAINT moped_entity_department_id_fkey FOREIGN KEY ("department_id") REFERENCES moped_department ("department_id"); + +-- Add foreign key constraints to entity table workgroup_id column +ALTER TABLE moped_entity +ADD CONSTRAINT moped_entity_workgroup_id_fkey FOREIGN KEY ("workgroup_id") +REFERENCES moped_workgroup ("workgroup_id"); + +-- Add foreign key constraints to department and entity tables for organization_id columns +ALTER TABLE moped_entity +ADD CONSTRAINT moped_entity_organization_id_fkey FOREIGN KEY ("organization_id") +REFERENCES moped_organization ("organization_id"); + +ALTER TABLE moped_department +ADD CONSTRAINT moped_entity_organization_id_fkey FOREIGN KEY ("organization_id") +REFERENCES moped_organization ("organization_id"); From eb78619cf311b9feb33e3dbeaa8c1cd1a462ad78 Mon Sep 17 00:00:00 2001 From: chiaberry Date: Mon, 2 Dec 2024 12:16:07 -0600 Subject: [PATCH 48/85] use the full project name in modals --- moped-editor/src/views/dashboard/DashboardView.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/moped-editor/src/views/dashboard/DashboardView.js b/moped-editor/src/views/dashboard/DashboardView.js index e626e3d18e..4d86a64738 100644 --- a/moped-editor/src/views/dashboard/DashboardView.js +++ b/moped-editor/src/views/dashboard/DashboardView.js @@ -202,7 +202,7 @@ const DashboardView = () => { { render: (entry) => ( { Date: Mon, 2 Dec 2024 13:48:53 -0600 Subject: [PATCH 49/85] pin edit columns to right, use default icon style var --- .../projectView/ProjectSummary/SubprojectsTable.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectsTable.js b/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectsTable.js index 2d5859f272..ad6078241e 100644 --- a/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectsTable.js +++ b/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectsTable.js @@ -19,6 +19,7 @@ import ProjectStatusBadge from "../../projectView/ProjectStatusBadge"; import SubprojectLookupComponent from "./SubprojectLookupComponent"; import DeleteConfirmationModal from "../DeleteConfirmationModal"; import RenderFieldLink from "src/components/RenderFieldLink"; +import { defaultEditColumnIconStyle } from "src/utils/dataGridHelpers"; import { SUBPROJECT_QUERY, @@ -90,7 +91,7 @@ const useColumns = ({ if (isInEditMode) { return [ } + icon={} label="Save" sx={{ color: "primary.main", @@ -98,7 +99,7 @@ const useColumns = ({ onClick={handleSaveClick(id)} />, } + icon={} label="Cancel" className="textPrimary" onClick={handleCancelClick(id)} @@ -108,7 +109,7 @@ const useColumns = ({ } return [ } + icon={} label="Delete" onClick={() => handleDeleteOpen(id)} color="inherit" @@ -291,6 +292,7 @@ const SubprojectsTable = ({ projectId = null, refetchSummaryData }) => { hideFooter disableRowSelectionOnClick localeText={{ noRowsLabel: "No subprojects to display" }} + initialState={{ pinnedColumns: { right: ["edit"] } }} onRowEditStart={(params, event) => { event.defaultMuiPrevented = true; // disable editing rows }} From 968dcf61ed31b3d451dcdd04359e35803ca34089 Mon Sep 17 00:00:00 2001 From: rose Date: Mon, 2 Dec 2024 13:49:05 -0600 Subject: [PATCH 50/85] remove helper text --- .../projectView/ProjectSummary/SubprojectLookupComponent.js | 1 - 1 file changed, 1 deletion(-) diff --git a/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectLookupComponent.js b/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectLookupComponent.js index 60bc6b9b18..d6b93944c5 100644 --- a/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectLookupComponent.js +++ b/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectLookupComponent.js @@ -46,7 +46,6 @@ const SubprojectLookupComponent = ({ id, value, field, hasFocus, data }) => { )} /> - Required ); }; From 5fb6d086ce30b7c842da7a80754a9e674b7baac1 Mon Sep 17 00:00:00 2001 From: rose Date: Mon, 2 Dec 2024 13:53:10 -0600 Subject: [PATCH 51/85] remove empty tag --- .../ProjectSummary/SubprojectsToolbar.js | 28 +++++++++---------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectsToolbar.js b/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectsToolbar.js index 26f9346afe..41ff8074e7 100644 --- a/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectsToolbar.js +++ b/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectsToolbar.js @@ -4,21 +4,19 @@ import Box from "@mui/material/Box"; import Typography from "@mui/material/Typography"; /** Custom toolbar title that resembles the material table titles we used */ const SubprojectsToolbar = ({ onClick }) => ( - <> - - - Subprojects - - - - + + + Subprojects + + + ); export default SubprojectsToolbar; From b9ed889d05350f86ad9fde34a528f07af8c5470a Mon Sep 17 00:00:00 2001 From: rose Date: Mon, 2 Dec 2024 14:05:40 -0600 Subject: [PATCH 52/85] remove this import --- .../projectView/ProjectSummary/SubprojectLookupComponent.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectLookupComponent.js b/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectLookupComponent.js index d6b93944c5..33de063f05 100644 --- a/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectLookupComponent.js +++ b/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectLookupComponent.js @@ -1,6 +1,6 @@ import React, { useEffect, useRef } from "react"; import { useGridApiContext } from "@mui/x-data-grid-pro"; -import { FormControl, FormHelperText, TextField } from "@mui/material"; +import { FormControl, TextField } from "@mui/material"; import Autocomplete from "@mui/material/Autocomplete"; /** Component for subproject lookup dropdown From ed8b209f35c623c02c15032988a3df471b798336 Mon Sep 17 00:00:00 2001 From: rose Date: Mon, 2 Dec 2024 14:31:21 -0600 Subject: [PATCH 53/85] add comment about clickaway listener bug --- moped-editor/src/components/GridTable/Search.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/moped-editor/src/components/GridTable/Search.js b/moped-editor/src/components/GridTable/Search.js index efa19d9fcd..0c8573a02a 100644 --- a/moped-editor/src/components/GridTable/Search.js +++ b/moped-editor/src/components/GridTable/Search.js @@ -245,6 +245,9 @@ const Search = ({ placement={"bottom"} className={classes.advancedSearchRoot} > + {/* FYI: you cannot use a Select component inside the click away listener + as discussed in this thread https://github.com/mui/material-ui/issues/25578 + so we have opted to use Autocompletes instead*/} Date: Mon, 2 Dec 2024 15:56:29 -0600 Subject: [PATCH 54/85] Delete outdated row intended for null department --- .../migrations/1732661952305_department_reorg/up.sql | 3 +++ 1 file changed, 3 insertions(+) diff --git a/moped-database/migrations/1732661952305_department_reorg/up.sql b/moped-database/migrations/1732661952305_department_reorg/up.sql index ef1d46a8b1..55edb0c7b5 100644 --- a/moped-database/migrations/1732661952305_department_reorg/up.sql +++ b/moped-database/migrations/1732661952305_department_reorg/up.sql @@ -82,3 +82,6 @@ REFERENCES moped_organization ("organization_id"); ALTER TABLE moped_department ADD CONSTRAINT moped_entity_organization_id_fkey FOREIGN KEY ("organization_id") REFERENCES moped_organization ("organization_id"); + +-- Remove row with 0 for id and 'None' for name; we're not using it and null is more appropriate for entities and workgroups with no department +DELETE FROM moped_department WHERE department_id = 0; From 4937eb152324334859d26eec91e99f8a9c627186 Mon Sep 17 00:00:00 2001 From: Mike Date: Mon, 2 Dec 2024 16:36:29 -0600 Subject: [PATCH 55/85] Adding in the missing ETL readme here --- moped-etl/README.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 moped-etl/README.md diff --git a/moped-etl/README.md b/moped-etl/README.md new file mode 100644 index 0000000000..fdfdb4c908 --- /dev/null +++ b/moped-etl/README.md @@ -0,0 +1,3 @@ +# Moped ETLs + +These ETLs are scheduled through our [Airflow](https://github.com/cityofaustin/atd-airflow) deployment. Each folder contains a containerized Python script and the Docker images are updated through Github Action workflows. From 4759d5ce11bf700e280dbac94a2f4a310fb8b6e1 Mon Sep 17 00:00:00 2001 From: Mike Date: Mon, 2 Dec 2024 18:12:51 -0600 Subject: [PATCH 56/85] Add up/down migrations to add comp work types --- .../down.sql | 542 ++++++++++++++++++ .../1733184698645_add_comp_work_types/up.sql | 542 ++++++++++++++++++ 2 files changed, 1084 insertions(+) create mode 100644 moped-database/migrations/1733184698645_add_comp_work_types/down.sql create mode 100644 moped-database/migrations/1733184698645_add_comp_work_types/up.sql diff --git a/moped-database/migrations/1733184698645_add_comp_work_types/down.sql b/moped-database/migrations/1733184698645_add_comp_work_types/down.sql new file mode 100644 index 0000000000..c650974aa0 --- /dev/null +++ b/moped-database/migrations/1733184698645_add_comp_work_types/down.sql @@ -0,0 +1,542 @@ +DROP VIEW IF EXISTS exploded_component_arcgis_online_view; +DROP VIEW IF EXISTS component_arcgis_online_view; +DROP VIEW IF EXISTS project_list_view; + +CREATE OR REPLACE VIEW project_list_view AS WITH project_person_list_lookup AS ( + SELECT + mpp.project_id, + string_agg(DISTINCT concat(mu.first_name, ' ', mu.last_name, ':', mpr.project_role_name), ','::text) AS project_team_members + FROM moped_proj_personnel mpp + JOIN moped_users mu ON mpp.user_id = mu.user_id + JOIN moped_proj_personnel_roles mppr ON mpp.project_personnel_id = mppr.project_personnel_id + JOIN moped_project_roles mpr ON mppr.project_role_id = mpr.project_role_id + WHERE mpp.is_deleted = false AND mppr.is_deleted = false + GROUP BY mpp.project_id +), + +funding_sources_lookup AS ( + SELECT + mpf.project_id, + string_agg(DISTINCT mfs.funding_source_name, ', '::text ORDER BY mfs.funding_source_name) AS funding_source_name, + string_agg(DISTINCT mfp.funding_program_name, ', '::text ORDER BY mfp.funding_program_name) AS funding_program_names, + string_agg( + DISTINCT + CASE + WHEN mfs.funding_source_name IS NOT null AND mfp.funding_program_name IS NOT null THEN concat(mfs.funding_source_name, ' - ', mfp.funding_program_name) + WHEN mfs.funding_source_name IS NOT null THEN mfs.funding_source_name + WHEN mfp.funding_program_name IS NOT null THEN mfp.funding_program_name + ELSE null::text + END, ', '::text ORDER BY ( + CASE + WHEN mfs.funding_source_name IS NOT null AND mfp.funding_program_name IS NOT null THEN concat(mfs.funding_source_name, ' - ', mfp.funding_program_name) + WHEN mfs.funding_source_name IS NOT null THEN mfs.funding_source_name + WHEN mfp.funding_program_name IS NOT null THEN mfp.funding_program_name + ELSE null::text + END + ) + ) AS funding_source_and_program_names + FROM moped_proj_funding mpf + LEFT JOIN moped_fund_sources mfs ON mpf.funding_source_id = mfs.funding_source_id + LEFT JOIN moped_fund_programs mfp ON mpf.funding_program_id = mfp.funding_program_id + WHERE mpf.is_deleted = false + GROUP BY mpf.project_id +), + +project_type_lookup AS ( + SELECT + mpt.project_id, + string_agg(mt.type_name, ', '::text) AS type_name + FROM moped_project_types mpt + LEFT JOIN moped_types mt ON mpt.project_type_id = mt.type_id AND mpt.is_deleted = false + GROUP BY mpt.project_id +), + +child_project_lookup AS ( + SELECT + jsonb_agg(children.project_id) AS children_project_ids, + children.parent_project_id AS parent_id + FROM moped_project children + JOIN moped_project parent ON children.parent_project_id = parent.project_id + WHERE children.is_deleted = false + GROUP BY children.parent_project_id +), + +work_activities AS ( + SELECT + mpwa.project_id, + string_agg(task_order_objects.task_order_object ->> 'display_name'::text, ', '::text) AS task_order_names, + string_agg(task_order_objects.task_order_object ->> 'task_order'::text, ', '::text) AS task_order_names_short, + jsonb_agg(DISTINCT task_order_objects.task_order_object) FILTER (WHERE task_order_objects.task_order_object IS NOT null) AS task_orders, + string_agg(DISTINCT mpwa.workgroup_contractor, ', '::text) AS workgroup_contractors, + string_agg(mpwa.contract_number, ', '::text) AS contract_numbers + FROM moped_proj_work_activity mpwa + LEFT JOIN LATERAL jsonb_array_elements(mpwa.task_orders) task_order_objects (task_order_object) ON true + WHERE 1 = 1 AND mpwa.is_deleted = false + GROUP BY mpwa.project_id +), + +moped_proj_components_subtypes AS ( + SELECT + mpc.project_id, + string_agg(DISTINCT mc.component_name_full, ', '::text) AS components + FROM moped_proj_components mpc + LEFT JOIN moped_components mc ON mpc.component_id = mc.component_id + WHERE mpc.is_deleted = false + GROUP BY mpc.project_id +), + +project_district_association AS ( + WITH project_council_district_map AS ( + SELECT DISTINCT + moped_project.project_id, + features_council_districts.council_district_id + FROM moped_project + LEFT JOIN moped_proj_components ON moped_project.project_id = moped_proj_components.project_id + LEFT JOIN features ON moped_proj_components.project_component_id = features.component_id + LEFT JOIN features_council_districts ON features.id = features_council_districts.feature_id + WHERE features.is_deleted IS false AND moped_proj_components.is_deleted IS false + ), + +parent_child_project_map AS ( + SELECT + parent_projects.project_id, + unnest(ARRAY[parent_projects.project_id] || array_agg(child_projects.project_id)) AS self_and_children_project_ids + FROM moped_project parent_projects + LEFT JOIN moped_project child_projects ON parent_projects.project_id = child_projects.parent_project_id + GROUP BY parent_projects.project_id + ORDER BY parent_projects.project_id + ) + + SELECT + projects.project_id, + array_agg(DISTINCT project_districts.council_district_id) FILTER (WHERE project_districts.council_district_id IS NOT null) AS project_council_districts, + array_agg(DISTINCT project_and_children_districts.council_district_id) FILTER (WHERE project_and_children_districts.council_district_id IS NOT null) AS project_and_child_project_council_districts + FROM parent_child_project_map projects + LEFT JOIN project_council_district_map project_and_children_districts ON projects.self_and_children_project_ids = project_and_children_districts.project_id + LEFT JOIN project_council_district_map project_districts ON projects.project_id = project_districts.project_id + GROUP BY projects.project_id +), + +min_confirmed_phase_dates AS ( + WITH min_dates AS ( + SELECT + phases.project_id, + min(phases.phase_start) AS min_date + FROM moped_proj_phases phases + LEFT JOIN moped_phases ON phases.phase_id = moped_phases.phase_id + WHERE true AND phases.phase_start IS NOT null AND phases.is_phase_start_confirmed = true AND moped_phases.phase_name_simple = 'Complete'::text AND phases.is_deleted = false + GROUP BY phases.project_id + UNION ALL + SELECT + phases.project_id, + min(phases.phase_end) AS min_date + FROM moped_proj_phases phases + LEFT JOIN moped_phases ON phases.phase_id = moped_phases.phase_id + WHERE true AND phases.phase_end IS NOT null AND phases.is_phase_end_confirmed = true AND moped_phases.phase_name_simple = 'Complete'::text AND phases.is_deleted = false + GROUP BY phases.project_id + ) + + SELECT + min_dates.project_id, + min(min_dates.min_date) AS min_phase_date + FROM min_dates + GROUP BY min_dates.project_id +), + +min_estimated_phase_dates AS ( + WITH min_dates AS ( + SELECT + phases.project_id, + min(phases.phase_start) AS min_date + FROM moped_proj_phases phases + LEFT JOIN moped_phases ON phases.phase_id = moped_phases.phase_id + WHERE true AND phases.phase_start IS NOT null AND phases.is_phase_start_confirmed = false AND moped_phases.phase_name_simple = 'Complete'::text AND phases.is_deleted = false + GROUP BY phases.project_id + UNION ALL + SELECT + phases.project_id, + min(phases.phase_end) AS min_date + FROM moped_proj_phases phases + LEFT JOIN moped_phases ON phases.phase_id = moped_phases.phase_id + WHERE true AND phases.phase_end IS NOT null AND phases.is_phase_end_confirmed = false AND moped_phases.phase_name_simple = 'Complete'::text AND phases.is_deleted = false + GROUP BY phases.project_id + ) + + SELECT + min_dates.project_id, + min(min_dates.min_date) AS min_phase_date + FROM min_dates + GROUP BY min_dates.project_id +) + +SELECT + mp.project_id, + mp.project_name_full, + mp.project_name, + mp.project_name_secondary, + mp.project_description, + mp.ecapris_subproject_id, + mp.project_website, + mp.date_added, + mp.is_deleted, + mp.updated_at, + current_phase.phase_name AS current_phase, + current_phase.phase_key AS current_phase_key, + current_phase.phase_name_simple AS current_phase_simple, + ppll.project_team_members, + me.entity_name AS project_sponsor, + mel.entity_name AS project_lead, + mpps.name AS public_process_status, + mp.interim_project_id, + mp.parent_project_id, + mp.knack_project_id, + 'https://mobility.austin.gov/moped/projects/'::text || mp.project_id::text AS project_url, + 'https://mobility.austin.gov/moped/projects/'::text || mp.parent_project_id::text AS parent_project_url, + proj_status_update.project_note AS project_status_update, + proj_status_update.date_created AS project_status_update_date_created, + work_activities.workgroup_contractors, + work_activities.contract_numbers, + work_activities.task_order_names, + work_activities.task_order_names_short, + work_activities.task_orders, + ( + SELECT moped_project.project_name_full + FROM moped_project + WHERE moped_project.project_id = mp.parent_project_id + ) AS parent_project_name, + cpl.children_project_ids, + string_agg(DISTINCT me2.entity_name, ', '::text) AS project_partners, + ( + SELECT json_agg(json_build_object('signal_id', feature_signals.signal_id, 'knack_id', feature_signals.knack_id, 'location_name', feature_signals.location_name, 'signal_type', feature_signals.signal_type, 'id', feature_signals.id)) AS json_agg + FROM moped_proj_components components + LEFT JOIN feature_signals ON components.project_component_id = feature_signals.component_id + WHERE true AND components.is_deleted = false AND components.project_id = mp.project_id AND feature_signals.signal_id IS NOT null AND feature_signals.is_deleted = false + ) AS project_feature, + fsl.funding_source_name, + fsl.funding_program_names, + fsl.funding_source_and_program_names, + ptl.type_name, + ( + SELECT min(phases.phase_start) AS min + FROM moped_proj_phases phases + WHERE true AND phases.project_id = mp.project_id AND phases.phase_id = 9 AND phases.is_deleted = false + ) AS construction_start_date, + mcpd.min_phase_date AS substantial_completion_date, + CASE + WHEN mcpd.min_phase_date IS NOT null THEN null::timestamp with time zone + ELSE mepd.min_phase_date + END AS substantial_completion_date_estimated, + ( + SELECT string_agg(concat(users.first_name, ' ', users.last_name), ', '::text) AS string_agg + FROM moped_proj_personnel mpp + JOIN moped_users users ON mpp.user_id = users.user_id + JOIN moped_proj_personnel_roles mppr ON mpp.project_personnel_id = mppr.project_personnel_id + JOIN moped_project_roles mpr ON mppr.project_role_id = mpr.project_role_id + WHERE 1 = 1 AND mpr.project_role_name = 'Inspector'::text AND mpp.is_deleted = false AND mppr.is_deleted = false AND mpp.project_id = mp.project_id + GROUP BY mpp.project_id + ) AS project_inspector, + ( + SELECT string_agg(concat(users.first_name, ' ', users.last_name), ', '::text) AS string_agg + FROM moped_proj_personnel mpp + JOIN moped_users users ON mpp.user_id = users.user_id + JOIN moped_proj_personnel_roles mppr ON mpp.project_personnel_id = mppr.project_personnel_id + JOIN moped_project_roles mpr ON mppr.project_role_id = mpr.project_role_id + WHERE 1 = 1 AND mpr.project_role_name = 'Designer'::text AND mpp.is_deleted = false AND mppr.is_deleted = false AND mpp.project_id = mp.project_id + GROUP BY mpp.project_id + ) AS project_designer, + ( + SELECT string_agg(tags.name, ', '::text) AS string_agg + FROM moped_proj_tags ptags + JOIN moped_tags tags ON ptags.tag_id = tags.id + WHERE 1 = 1 AND ptags.is_deleted = false AND ptags.project_id = mp.project_id + GROUP BY ptags.project_id + ) AS project_tags, + concat(added_by_user.first_name, ' ', added_by_user.last_name) AS added_by, + mpcs.components, + districts.project_council_districts, + districts.project_and_child_project_council_districts +FROM moped_project mp +LEFT JOIN project_person_list_lookup ppll ON mp.project_id = ppll.project_id +LEFT JOIN funding_sources_lookup fsl ON mp.project_id = fsl.project_id +LEFT JOIN project_type_lookup ptl ON mp.project_id = ptl.project_id +LEFT JOIN moped_entity me ON mp.project_sponsor = me.entity_id +LEFT JOIN moped_entity mel ON mp.project_lead_id = mel.entity_id +LEFT JOIN moped_proj_partners mpp2 ON mp.project_id = mpp2.project_id AND mpp2.is_deleted = false +LEFT JOIN moped_entity me2 ON mpp2.entity_id = me2.entity_id +LEFT JOIN work_activities ON mp.project_id = work_activities.project_id +LEFT JOIN moped_users added_by_user ON mp.added_by = added_by_user.user_id +LEFT JOIN current_phase_view current_phase ON mp.project_id = current_phase.project_id +LEFT JOIN moped_public_process_statuses mpps ON mp.public_process_status_id = mpps.id +LEFT JOIN child_project_lookup cpl ON mp.project_id = cpl.parent_id +LEFT JOIN moped_proj_components_subtypes mpcs ON mp.project_id = mpcs.project_id +LEFT JOIN project_district_association districts ON mp.project_id = districts.project_id +LEFT JOIN min_confirmed_phase_dates mcpd ON mp.project_id = mcpd.project_id +LEFT JOIN min_estimated_phase_dates mepd ON mp.project_id = mepd.project_id +LEFT JOIN LATERAL ( + SELECT + mpn.project_note, + mpn.created_at AS date_created + FROM moped_proj_notes mpn + WHERE mpn.project_id = mp.project_id AND mpn.project_note_type = 2 AND mpn.is_deleted = false + ORDER BY mpn.created_at DESC + LIMIT 1 +) proj_status_update ON true +WHERE mp.is_deleted = false +GROUP BY mp.project_id, mp.project_name, mp.project_description, ppll.project_team_members, mp.ecapris_subproject_id, mp.date_added, mp.is_deleted, me.entity_name, mel.entity_name, mp.updated_at, mp.interim_project_id, mp.parent_project_id, mp.knack_project_id, current_phase.phase_name, current_phase.phase_key, current_phase.phase_name_simple, ptl.type_name, mpcs.components, fsl.funding_source_name, fsl.funding_program_names, fsl.funding_source_and_program_names, added_by_user.first_name, added_by_user.last_name, mpps.name, cpl.children_project_ids, proj_status_update.project_note, proj_status_update.date_created, work_activities.workgroup_contractors, work_activities.contract_numbers, work_activities.task_order_names, work_activities.task_order_names_short, work_activities.task_orders, districts.project_council_districts, districts.project_and_child_project_council_districts, mepd.min_phase_date, mcpd.min_phase_date; + +CREATE OR REPLACE VIEW component_arcgis_online_view AS WITH work_types AS ( + SELECT + mpcwt.project_component_id, + string_agg(mwt.name, ', '::text) AS work_types + FROM moped_proj_component_work_types mpcwt + LEFT JOIN moped_work_types mwt ON mpcwt.work_type_id = mwt.id + WHERE mpcwt.is_deleted = false + GROUP BY mpcwt.project_component_id +), + +council_districts AS ( + SELECT + features.component_id AS project_component_id, + string_agg(DISTINCT features_council_districts.council_district_id::text, ', '::text) AS council_districts, + string_agg(DISTINCT lpad(features_council_districts.council_district_id::text, 2, '0'::text), ', '::text) AS council_districts_searchable + FROM features_council_districts + LEFT JOIN features ON features_council_districts.feature_id = features.id + WHERE features.is_deleted = false + GROUP BY features.component_id +), + +comp_geography AS ( + SELECT + feature_union.component_id AS project_component_id, + string_agg(DISTINCT feature_union.id::text, ', '::text) AS feature_ids, + st_asgeojson(st_multi(st_union(array_agg(feature_union.geography))))::json AS geometry, + st_asgeojson(st_multi(st_union(array_agg(feature_union.line_geography))))::json AS line_geometry, + string_agg(DISTINCT feature_union.signal_id::text, ', '::text) AS signal_ids, + sum(feature_union.length_feet) AS length_feet_total + FROM ( + SELECT + feature_signals.id, + feature_signals.component_id, + feature_signals.geography::geometry AS geography, + st_exteriorring(st_buffer(feature_signals.geography, 7::double precision)::geometry) AS line_geography, + feature_signals.signal_id, + null::integer AS length_feet + FROM feature_signals + WHERE feature_signals.is_deleted = false + UNION ALL + SELECT + feature_street_segments.id, + feature_street_segments.component_id, + feature_street_segments.geography::geometry AS geography, + feature_street_segments.geography::geometry AS line_geography, + null::integer AS signal_id, + feature_street_segments.length_feet + FROM feature_street_segments + WHERE feature_street_segments.is_deleted = false + UNION ALL + SELECT + feature_intersections.id, + feature_intersections.component_id, + feature_intersections.geography::geometry AS geography, + st_exteriorring(st_buffer(feature_intersections.geography, 7::double precision)::geometry) AS line_geography, + null::integer AS signal_id, + null::integer AS length_feet + FROM feature_intersections + WHERE feature_intersections.is_deleted = false + UNION ALL + SELECT + feature_drawn_points.id, + feature_drawn_points.component_id, + feature_drawn_points.geography::geometry AS geography, + st_exteriorring(st_buffer(feature_drawn_points.geography, 7::double precision)::geometry) AS line_geography, + null::integer AS signal_id, + null::integer AS length_feet + FROM feature_drawn_points + WHERE feature_drawn_points.is_deleted = false + UNION ALL + SELECT + feature_drawn_lines.id, + feature_drawn_lines.component_id, + feature_drawn_lines.geography::geometry AS geography, + feature_drawn_lines.geography::geometry AS line_geography, + null::integer AS signal_id, + feature_drawn_lines.length_feet + FROM feature_drawn_lines + WHERE feature_drawn_lines.is_deleted = false + UNION ALL + SELECT + feature_school_beacons.id, + feature_school_beacons.component_id, + feature_school_beacons.geography::geometry AS geography, + st_exteriorring(st_buffer(feature_school_beacons.geography, 7::double precision)::geometry) AS line_geography, + null::integer AS signal_id, + null::integer AS length_feet + FROM feature_school_beacons + WHERE feature_school_beacons.is_deleted = false + ) feature_union + GROUP BY feature_union.component_id +), + +subcomponents AS ( + SELECT + mpcs.project_component_id, + string_agg(ms.subcomponent_name, ', '::text) AS subcomponents + FROM moped_proj_components_subcomponents mpcs + LEFT JOIN moped_subcomponents ms ON mpcs.subcomponent_id = ms.subcomponent_id + WHERE mpcs.is_deleted = false + GROUP BY mpcs.project_component_id +), + +component_tags AS ( + SELECT + mpct.project_component_id, + string_agg((mct.type || ' - '::text) || mct.name, ', '::text) AS component_tags + FROM moped_proj_component_tags mpct + LEFT JOIN moped_component_tags mct ON mpct.component_tag_id = mct.id + WHERE mpct.is_deleted = false + GROUP BY mpct.project_component_id +), + +related_projects AS ( + SELECT + pmp.project_id, + concat_ws(', '::text, pmp.project_id, string_agg(cmp.project_id::text, ', '::text)) AS related_project_ids_with_self, + concat_ws(', '::text, lpad(pmp.project_id::text, 5, '0'::text), string_agg(lpad(cmp.project_id::text, 5, '0'::text), ', '::text)) AS related_project_ids_searchable_with_self + FROM moped_project pmp + LEFT JOIN moped_project cmp ON pmp.project_id = cmp.parent_project_id + WHERE cmp.is_deleted = false + GROUP BY pmp.project_id +), + +latest_public_meeting_date AS ( + SELECT + mpm.project_id, + coalesce(max(mpm.date_actual), max(mpm.date_estimate)) AS latest + FROM moped_proj_milestones mpm + WHERE mpm.milestone_id = 65 AND mpm.is_deleted = false + GROUP BY mpm.project_id +), + +earliest_active_or_construction_phase_date AS ( + SELECT + mpp.project_id, + min(mpp.phase_start) AS earliest + FROM moped_proj_phases mpp + LEFT JOIN moped_phases mp ON mpp.phase_id = mp.phase_id + WHERE (mp.phase_name_simple = any(ARRAY['Active'::text, 'Construction'::text])) AND mpp.is_deleted = false + GROUP BY mpp.project_id +) + +SELECT + mpc.project_id, + comp_geography.project_component_id, + comp_geography.feature_ids, + mpc.component_id, + comp_geography.geometry, + comp_geography.line_geometry, + comp_geography.signal_ids, + council_districts.council_districts, + council_districts.council_districts_searchable, + NOT coalesce(council_districts.council_districts IS null OR council_districts.council_districts = ''::text, false) AS is_within_city_limits, + comp_geography.length_feet_total, + round(comp_geography.length_feet_total::numeric / 5280::numeric, 2) AS length_miles_total, + mc.component_name, + mc.component_subtype, + mc.component_name_full, + 'placeholder text'::text AS component_categories, + CASE + WHEN mc.line_representation = true THEN 'Line'::text + ELSE 'Point'::text + END AS geometry_type, + subcomponents.subcomponents AS component_subcomponents, + work_types.work_types AS component_work_types, + component_tags.component_tags, + mpc.description AS component_description, + mpc.interim_project_component_id, + coalesce(mpc.completion_date, plv.substantial_completion_date) AS substantial_completion_date, + plv.substantial_completion_date_estimated, + mpc.srts_id, + mpc.location_description AS component_location_description, + plv.project_name, + plv.project_name_secondary, + plv.project_name_full, + plv.project_description, + plv.ecapris_subproject_id, + plv.project_website, + plv.updated_at AS project_updated_at, + mpc.phase_id AS component_phase_id, + mph.phase_name AS component_phase_name, + mph.phase_name_simple AS component_phase_name_simple, + current_phase.phase_id AS project_phase_id, + current_phase.phase_name AS project_phase_name, + current_phase.phase_name_simple AS project_phase_name_simple, + coalesce(mph.phase_name, current_phase.phase_name) AS current_phase_name, + coalesce(mph.phase_name_simple, current_phase.phase_name_simple) AS current_phase_name_simple, + plv.project_team_members, + plv.project_sponsor, + plv.project_lead, + plv.public_process_status, + plv.interim_project_id, + plv.project_partners, + plv.task_order_names, + plv.funding_source_and_program_names AS funding_sources, + plv.type_name, + plv.project_status_update, + plv.project_status_update_date_created, + to_char(timezone('US/Central'::text, plv.construction_start_date), 'YYYY-MM-DD'::text) AS construction_start_date, + plv.project_inspector, + plv.project_designer, + plv.project_tags, + plv.workgroup_contractors, + plv.contract_numbers, + plv.parent_project_id, + plv.parent_project_name, + plv.parent_project_url, + plv.parent_project_name AS parent_project_name_full, + rp.related_project_ids_with_self AS related_project_ids, + rp.related_project_ids_searchable_with_self AS related_project_ids_searchable, + plv.knack_project_id AS knack_data_tracker_project_record_id, + plv.project_url, + (plv.project_url || '?tab=map&project_component_id='::text) || mpc.project_component_id::text AS component_url, + get_project_development_status(lpmd.latest::timestamp with time zone, eaocpd.earliest, coalesce(mpc.completion_date, plv.substantial_completion_date), plv.substantial_completion_date_estimated, coalesce(mph.phase_name_simple, current_phase.phase_name_simple)) AS project_development_status, + project_development_status_date.result AS project_development_status_date, + to_char(project_development_status_date.result, 'YYYY'::text)::integer AS project_development_status_date_calendar_year, + to_char(project_development_status_date.result, 'FMMonth YYYY'::text) AS project_development_status_date_calendar_year_month, + to_char(project_development_status_date.result, 'YYYY-MM'::text) AS project_development_status_date_calendar_year_month_numeric, + date_part('quarter'::text, project_development_status_date.result)::text AS project_development_status_date_calendar_year_quarter, + CASE + WHEN date_part('quarter'::text, project_development_status_date.result) = 4::double precision THEN (to_char(project_development_status_date.result, 'YYYY'::text)::integer + 1)::text + ELSE to_char(project_development_status_date.result, 'YYYY'::text) + END AS project_development_status_date_fiscal_year, + CASE + WHEN date_part('quarter'::text, project_development_status_date.result) = 4::double precision THEN 1::double precision + ELSE date_part('quarter'::text, project_development_status_date.result) + 1::double precision + END::text AS project_development_status_date_fiscal_year_quarter, + plv.added_by AS project_added_by +FROM moped_proj_components mpc +LEFT JOIN comp_geography ON mpc.project_component_id = comp_geography.project_component_id +LEFT JOIN council_districts ON mpc.project_component_id = council_districts.project_component_id +LEFT JOIN subcomponents ON mpc.project_component_id = subcomponents.project_component_id +LEFT JOIN work_types ON mpc.project_component_id = work_types.project_component_id +LEFT JOIN component_tags ON mpc.project_component_id = component_tags.project_component_id +LEFT JOIN project_list_view plv ON mpc.project_id = plv.project_id +LEFT JOIN current_phase_view current_phase ON mpc.project_id = current_phase.project_id +LEFT JOIN moped_phases mph ON mpc.phase_id = mph.phase_id +LEFT JOIN moped_components mc ON mpc.component_id = mc.component_id +LEFT JOIN related_projects rp ON mpc.project_id = rp.project_id +LEFT JOIN latest_public_meeting_date lpmd ON mpc.project_id = lpmd.project_id +LEFT JOIN earliest_active_or_construction_phase_date eaocpd ON mpc.project_id = eaocpd.project_id +LEFT JOIN LATERAL (SELECT timezone('US/Central'::text, get_project_development_status_date(lpmd.latest::timestamp with time zone, eaocpd.earliest, coalesce(mpc.completion_date, plv.substantial_completion_date), plv.substantial_completion_date_estimated, coalesce(mph.phase_name_simple, current_phase.phase_name_simple))) AS result) project_development_status_date ON true +WHERE mpc.is_deleted = false AND plv.is_deleted = false; + +CREATE OR REPLACE VIEW exploded_component_arcgis_online_view AS SELECT + component_arcgis_online_view.project_id, + component_arcgis_online_view.project_component_id, + st_geometrytype(dump.geom) AS geometry_type, + dump.path[1] AS point_index, + component_arcgis_online_view.geometry AS original_geometry, + st_asgeojson(dump.geom) AS exploded_geometry, + component_arcgis_online_view.project_updated_at +FROM component_arcgis_online_view, + LATERAL st_dump(st_geomfromgeojson(component_arcgis_online_view.geometry)) dump (path, geom) +WHERE st_geometrytype(st_geomfromgeojson(component_arcgis_online_view.geometry)) = 'ST_MultiPoint'::text; diff --git a/moped-database/migrations/1733184698645_add_comp_work_types/up.sql b/moped-database/migrations/1733184698645_add_comp_work_types/up.sql new file mode 100644 index 0000000000..c650974aa0 --- /dev/null +++ b/moped-database/migrations/1733184698645_add_comp_work_types/up.sql @@ -0,0 +1,542 @@ +DROP VIEW IF EXISTS exploded_component_arcgis_online_view; +DROP VIEW IF EXISTS component_arcgis_online_view; +DROP VIEW IF EXISTS project_list_view; + +CREATE OR REPLACE VIEW project_list_view AS WITH project_person_list_lookup AS ( + SELECT + mpp.project_id, + string_agg(DISTINCT concat(mu.first_name, ' ', mu.last_name, ':', mpr.project_role_name), ','::text) AS project_team_members + FROM moped_proj_personnel mpp + JOIN moped_users mu ON mpp.user_id = mu.user_id + JOIN moped_proj_personnel_roles mppr ON mpp.project_personnel_id = mppr.project_personnel_id + JOIN moped_project_roles mpr ON mppr.project_role_id = mpr.project_role_id + WHERE mpp.is_deleted = false AND mppr.is_deleted = false + GROUP BY mpp.project_id +), + +funding_sources_lookup AS ( + SELECT + mpf.project_id, + string_agg(DISTINCT mfs.funding_source_name, ', '::text ORDER BY mfs.funding_source_name) AS funding_source_name, + string_agg(DISTINCT mfp.funding_program_name, ', '::text ORDER BY mfp.funding_program_name) AS funding_program_names, + string_agg( + DISTINCT + CASE + WHEN mfs.funding_source_name IS NOT null AND mfp.funding_program_name IS NOT null THEN concat(mfs.funding_source_name, ' - ', mfp.funding_program_name) + WHEN mfs.funding_source_name IS NOT null THEN mfs.funding_source_name + WHEN mfp.funding_program_name IS NOT null THEN mfp.funding_program_name + ELSE null::text + END, ', '::text ORDER BY ( + CASE + WHEN mfs.funding_source_name IS NOT null AND mfp.funding_program_name IS NOT null THEN concat(mfs.funding_source_name, ' - ', mfp.funding_program_name) + WHEN mfs.funding_source_name IS NOT null THEN mfs.funding_source_name + WHEN mfp.funding_program_name IS NOT null THEN mfp.funding_program_name + ELSE null::text + END + ) + ) AS funding_source_and_program_names + FROM moped_proj_funding mpf + LEFT JOIN moped_fund_sources mfs ON mpf.funding_source_id = mfs.funding_source_id + LEFT JOIN moped_fund_programs mfp ON mpf.funding_program_id = mfp.funding_program_id + WHERE mpf.is_deleted = false + GROUP BY mpf.project_id +), + +project_type_lookup AS ( + SELECT + mpt.project_id, + string_agg(mt.type_name, ', '::text) AS type_name + FROM moped_project_types mpt + LEFT JOIN moped_types mt ON mpt.project_type_id = mt.type_id AND mpt.is_deleted = false + GROUP BY mpt.project_id +), + +child_project_lookup AS ( + SELECT + jsonb_agg(children.project_id) AS children_project_ids, + children.parent_project_id AS parent_id + FROM moped_project children + JOIN moped_project parent ON children.parent_project_id = parent.project_id + WHERE children.is_deleted = false + GROUP BY children.parent_project_id +), + +work_activities AS ( + SELECT + mpwa.project_id, + string_agg(task_order_objects.task_order_object ->> 'display_name'::text, ', '::text) AS task_order_names, + string_agg(task_order_objects.task_order_object ->> 'task_order'::text, ', '::text) AS task_order_names_short, + jsonb_agg(DISTINCT task_order_objects.task_order_object) FILTER (WHERE task_order_objects.task_order_object IS NOT null) AS task_orders, + string_agg(DISTINCT mpwa.workgroup_contractor, ', '::text) AS workgroup_contractors, + string_agg(mpwa.contract_number, ', '::text) AS contract_numbers + FROM moped_proj_work_activity mpwa + LEFT JOIN LATERAL jsonb_array_elements(mpwa.task_orders) task_order_objects (task_order_object) ON true + WHERE 1 = 1 AND mpwa.is_deleted = false + GROUP BY mpwa.project_id +), + +moped_proj_components_subtypes AS ( + SELECT + mpc.project_id, + string_agg(DISTINCT mc.component_name_full, ', '::text) AS components + FROM moped_proj_components mpc + LEFT JOIN moped_components mc ON mpc.component_id = mc.component_id + WHERE mpc.is_deleted = false + GROUP BY mpc.project_id +), + +project_district_association AS ( + WITH project_council_district_map AS ( + SELECT DISTINCT + moped_project.project_id, + features_council_districts.council_district_id + FROM moped_project + LEFT JOIN moped_proj_components ON moped_project.project_id = moped_proj_components.project_id + LEFT JOIN features ON moped_proj_components.project_component_id = features.component_id + LEFT JOIN features_council_districts ON features.id = features_council_districts.feature_id + WHERE features.is_deleted IS false AND moped_proj_components.is_deleted IS false + ), + +parent_child_project_map AS ( + SELECT + parent_projects.project_id, + unnest(ARRAY[parent_projects.project_id] || array_agg(child_projects.project_id)) AS self_and_children_project_ids + FROM moped_project parent_projects + LEFT JOIN moped_project child_projects ON parent_projects.project_id = child_projects.parent_project_id + GROUP BY parent_projects.project_id + ORDER BY parent_projects.project_id + ) + + SELECT + projects.project_id, + array_agg(DISTINCT project_districts.council_district_id) FILTER (WHERE project_districts.council_district_id IS NOT null) AS project_council_districts, + array_agg(DISTINCT project_and_children_districts.council_district_id) FILTER (WHERE project_and_children_districts.council_district_id IS NOT null) AS project_and_child_project_council_districts + FROM parent_child_project_map projects + LEFT JOIN project_council_district_map project_and_children_districts ON projects.self_and_children_project_ids = project_and_children_districts.project_id + LEFT JOIN project_council_district_map project_districts ON projects.project_id = project_districts.project_id + GROUP BY projects.project_id +), + +min_confirmed_phase_dates AS ( + WITH min_dates AS ( + SELECT + phases.project_id, + min(phases.phase_start) AS min_date + FROM moped_proj_phases phases + LEFT JOIN moped_phases ON phases.phase_id = moped_phases.phase_id + WHERE true AND phases.phase_start IS NOT null AND phases.is_phase_start_confirmed = true AND moped_phases.phase_name_simple = 'Complete'::text AND phases.is_deleted = false + GROUP BY phases.project_id + UNION ALL + SELECT + phases.project_id, + min(phases.phase_end) AS min_date + FROM moped_proj_phases phases + LEFT JOIN moped_phases ON phases.phase_id = moped_phases.phase_id + WHERE true AND phases.phase_end IS NOT null AND phases.is_phase_end_confirmed = true AND moped_phases.phase_name_simple = 'Complete'::text AND phases.is_deleted = false + GROUP BY phases.project_id + ) + + SELECT + min_dates.project_id, + min(min_dates.min_date) AS min_phase_date + FROM min_dates + GROUP BY min_dates.project_id +), + +min_estimated_phase_dates AS ( + WITH min_dates AS ( + SELECT + phases.project_id, + min(phases.phase_start) AS min_date + FROM moped_proj_phases phases + LEFT JOIN moped_phases ON phases.phase_id = moped_phases.phase_id + WHERE true AND phases.phase_start IS NOT null AND phases.is_phase_start_confirmed = false AND moped_phases.phase_name_simple = 'Complete'::text AND phases.is_deleted = false + GROUP BY phases.project_id + UNION ALL + SELECT + phases.project_id, + min(phases.phase_end) AS min_date + FROM moped_proj_phases phases + LEFT JOIN moped_phases ON phases.phase_id = moped_phases.phase_id + WHERE true AND phases.phase_end IS NOT null AND phases.is_phase_end_confirmed = false AND moped_phases.phase_name_simple = 'Complete'::text AND phases.is_deleted = false + GROUP BY phases.project_id + ) + + SELECT + min_dates.project_id, + min(min_dates.min_date) AS min_phase_date + FROM min_dates + GROUP BY min_dates.project_id +) + +SELECT + mp.project_id, + mp.project_name_full, + mp.project_name, + mp.project_name_secondary, + mp.project_description, + mp.ecapris_subproject_id, + mp.project_website, + mp.date_added, + mp.is_deleted, + mp.updated_at, + current_phase.phase_name AS current_phase, + current_phase.phase_key AS current_phase_key, + current_phase.phase_name_simple AS current_phase_simple, + ppll.project_team_members, + me.entity_name AS project_sponsor, + mel.entity_name AS project_lead, + mpps.name AS public_process_status, + mp.interim_project_id, + mp.parent_project_id, + mp.knack_project_id, + 'https://mobility.austin.gov/moped/projects/'::text || mp.project_id::text AS project_url, + 'https://mobility.austin.gov/moped/projects/'::text || mp.parent_project_id::text AS parent_project_url, + proj_status_update.project_note AS project_status_update, + proj_status_update.date_created AS project_status_update_date_created, + work_activities.workgroup_contractors, + work_activities.contract_numbers, + work_activities.task_order_names, + work_activities.task_order_names_short, + work_activities.task_orders, + ( + SELECT moped_project.project_name_full + FROM moped_project + WHERE moped_project.project_id = mp.parent_project_id + ) AS parent_project_name, + cpl.children_project_ids, + string_agg(DISTINCT me2.entity_name, ', '::text) AS project_partners, + ( + SELECT json_agg(json_build_object('signal_id', feature_signals.signal_id, 'knack_id', feature_signals.knack_id, 'location_name', feature_signals.location_name, 'signal_type', feature_signals.signal_type, 'id', feature_signals.id)) AS json_agg + FROM moped_proj_components components + LEFT JOIN feature_signals ON components.project_component_id = feature_signals.component_id + WHERE true AND components.is_deleted = false AND components.project_id = mp.project_id AND feature_signals.signal_id IS NOT null AND feature_signals.is_deleted = false + ) AS project_feature, + fsl.funding_source_name, + fsl.funding_program_names, + fsl.funding_source_and_program_names, + ptl.type_name, + ( + SELECT min(phases.phase_start) AS min + FROM moped_proj_phases phases + WHERE true AND phases.project_id = mp.project_id AND phases.phase_id = 9 AND phases.is_deleted = false + ) AS construction_start_date, + mcpd.min_phase_date AS substantial_completion_date, + CASE + WHEN mcpd.min_phase_date IS NOT null THEN null::timestamp with time zone + ELSE mepd.min_phase_date + END AS substantial_completion_date_estimated, + ( + SELECT string_agg(concat(users.first_name, ' ', users.last_name), ', '::text) AS string_agg + FROM moped_proj_personnel mpp + JOIN moped_users users ON mpp.user_id = users.user_id + JOIN moped_proj_personnel_roles mppr ON mpp.project_personnel_id = mppr.project_personnel_id + JOIN moped_project_roles mpr ON mppr.project_role_id = mpr.project_role_id + WHERE 1 = 1 AND mpr.project_role_name = 'Inspector'::text AND mpp.is_deleted = false AND mppr.is_deleted = false AND mpp.project_id = mp.project_id + GROUP BY mpp.project_id + ) AS project_inspector, + ( + SELECT string_agg(concat(users.first_name, ' ', users.last_name), ', '::text) AS string_agg + FROM moped_proj_personnel mpp + JOIN moped_users users ON mpp.user_id = users.user_id + JOIN moped_proj_personnel_roles mppr ON mpp.project_personnel_id = mppr.project_personnel_id + JOIN moped_project_roles mpr ON mppr.project_role_id = mpr.project_role_id + WHERE 1 = 1 AND mpr.project_role_name = 'Designer'::text AND mpp.is_deleted = false AND mppr.is_deleted = false AND mpp.project_id = mp.project_id + GROUP BY mpp.project_id + ) AS project_designer, + ( + SELECT string_agg(tags.name, ', '::text) AS string_agg + FROM moped_proj_tags ptags + JOIN moped_tags tags ON ptags.tag_id = tags.id + WHERE 1 = 1 AND ptags.is_deleted = false AND ptags.project_id = mp.project_id + GROUP BY ptags.project_id + ) AS project_tags, + concat(added_by_user.first_name, ' ', added_by_user.last_name) AS added_by, + mpcs.components, + districts.project_council_districts, + districts.project_and_child_project_council_districts +FROM moped_project mp +LEFT JOIN project_person_list_lookup ppll ON mp.project_id = ppll.project_id +LEFT JOIN funding_sources_lookup fsl ON mp.project_id = fsl.project_id +LEFT JOIN project_type_lookup ptl ON mp.project_id = ptl.project_id +LEFT JOIN moped_entity me ON mp.project_sponsor = me.entity_id +LEFT JOIN moped_entity mel ON mp.project_lead_id = mel.entity_id +LEFT JOIN moped_proj_partners mpp2 ON mp.project_id = mpp2.project_id AND mpp2.is_deleted = false +LEFT JOIN moped_entity me2 ON mpp2.entity_id = me2.entity_id +LEFT JOIN work_activities ON mp.project_id = work_activities.project_id +LEFT JOIN moped_users added_by_user ON mp.added_by = added_by_user.user_id +LEFT JOIN current_phase_view current_phase ON mp.project_id = current_phase.project_id +LEFT JOIN moped_public_process_statuses mpps ON mp.public_process_status_id = mpps.id +LEFT JOIN child_project_lookup cpl ON mp.project_id = cpl.parent_id +LEFT JOIN moped_proj_components_subtypes mpcs ON mp.project_id = mpcs.project_id +LEFT JOIN project_district_association districts ON mp.project_id = districts.project_id +LEFT JOIN min_confirmed_phase_dates mcpd ON mp.project_id = mcpd.project_id +LEFT JOIN min_estimated_phase_dates mepd ON mp.project_id = mepd.project_id +LEFT JOIN LATERAL ( + SELECT + mpn.project_note, + mpn.created_at AS date_created + FROM moped_proj_notes mpn + WHERE mpn.project_id = mp.project_id AND mpn.project_note_type = 2 AND mpn.is_deleted = false + ORDER BY mpn.created_at DESC + LIMIT 1 +) proj_status_update ON true +WHERE mp.is_deleted = false +GROUP BY mp.project_id, mp.project_name, mp.project_description, ppll.project_team_members, mp.ecapris_subproject_id, mp.date_added, mp.is_deleted, me.entity_name, mel.entity_name, mp.updated_at, mp.interim_project_id, mp.parent_project_id, mp.knack_project_id, current_phase.phase_name, current_phase.phase_key, current_phase.phase_name_simple, ptl.type_name, mpcs.components, fsl.funding_source_name, fsl.funding_program_names, fsl.funding_source_and_program_names, added_by_user.first_name, added_by_user.last_name, mpps.name, cpl.children_project_ids, proj_status_update.project_note, proj_status_update.date_created, work_activities.workgroup_contractors, work_activities.contract_numbers, work_activities.task_order_names, work_activities.task_order_names_short, work_activities.task_orders, districts.project_council_districts, districts.project_and_child_project_council_districts, mepd.min_phase_date, mcpd.min_phase_date; + +CREATE OR REPLACE VIEW component_arcgis_online_view AS WITH work_types AS ( + SELECT + mpcwt.project_component_id, + string_agg(mwt.name, ', '::text) AS work_types + FROM moped_proj_component_work_types mpcwt + LEFT JOIN moped_work_types mwt ON mpcwt.work_type_id = mwt.id + WHERE mpcwt.is_deleted = false + GROUP BY mpcwt.project_component_id +), + +council_districts AS ( + SELECT + features.component_id AS project_component_id, + string_agg(DISTINCT features_council_districts.council_district_id::text, ', '::text) AS council_districts, + string_agg(DISTINCT lpad(features_council_districts.council_district_id::text, 2, '0'::text), ', '::text) AS council_districts_searchable + FROM features_council_districts + LEFT JOIN features ON features_council_districts.feature_id = features.id + WHERE features.is_deleted = false + GROUP BY features.component_id +), + +comp_geography AS ( + SELECT + feature_union.component_id AS project_component_id, + string_agg(DISTINCT feature_union.id::text, ', '::text) AS feature_ids, + st_asgeojson(st_multi(st_union(array_agg(feature_union.geography))))::json AS geometry, + st_asgeojson(st_multi(st_union(array_agg(feature_union.line_geography))))::json AS line_geometry, + string_agg(DISTINCT feature_union.signal_id::text, ', '::text) AS signal_ids, + sum(feature_union.length_feet) AS length_feet_total + FROM ( + SELECT + feature_signals.id, + feature_signals.component_id, + feature_signals.geography::geometry AS geography, + st_exteriorring(st_buffer(feature_signals.geography, 7::double precision)::geometry) AS line_geography, + feature_signals.signal_id, + null::integer AS length_feet + FROM feature_signals + WHERE feature_signals.is_deleted = false + UNION ALL + SELECT + feature_street_segments.id, + feature_street_segments.component_id, + feature_street_segments.geography::geometry AS geography, + feature_street_segments.geography::geometry AS line_geography, + null::integer AS signal_id, + feature_street_segments.length_feet + FROM feature_street_segments + WHERE feature_street_segments.is_deleted = false + UNION ALL + SELECT + feature_intersections.id, + feature_intersections.component_id, + feature_intersections.geography::geometry AS geography, + st_exteriorring(st_buffer(feature_intersections.geography, 7::double precision)::geometry) AS line_geography, + null::integer AS signal_id, + null::integer AS length_feet + FROM feature_intersections + WHERE feature_intersections.is_deleted = false + UNION ALL + SELECT + feature_drawn_points.id, + feature_drawn_points.component_id, + feature_drawn_points.geography::geometry AS geography, + st_exteriorring(st_buffer(feature_drawn_points.geography, 7::double precision)::geometry) AS line_geography, + null::integer AS signal_id, + null::integer AS length_feet + FROM feature_drawn_points + WHERE feature_drawn_points.is_deleted = false + UNION ALL + SELECT + feature_drawn_lines.id, + feature_drawn_lines.component_id, + feature_drawn_lines.geography::geometry AS geography, + feature_drawn_lines.geography::geometry AS line_geography, + null::integer AS signal_id, + feature_drawn_lines.length_feet + FROM feature_drawn_lines + WHERE feature_drawn_lines.is_deleted = false + UNION ALL + SELECT + feature_school_beacons.id, + feature_school_beacons.component_id, + feature_school_beacons.geography::geometry AS geography, + st_exteriorring(st_buffer(feature_school_beacons.geography, 7::double precision)::geometry) AS line_geography, + null::integer AS signal_id, + null::integer AS length_feet + FROM feature_school_beacons + WHERE feature_school_beacons.is_deleted = false + ) feature_union + GROUP BY feature_union.component_id +), + +subcomponents AS ( + SELECT + mpcs.project_component_id, + string_agg(ms.subcomponent_name, ', '::text) AS subcomponents + FROM moped_proj_components_subcomponents mpcs + LEFT JOIN moped_subcomponents ms ON mpcs.subcomponent_id = ms.subcomponent_id + WHERE mpcs.is_deleted = false + GROUP BY mpcs.project_component_id +), + +component_tags AS ( + SELECT + mpct.project_component_id, + string_agg((mct.type || ' - '::text) || mct.name, ', '::text) AS component_tags + FROM moped_proj_component_tags mpct + LEFT JOIN moped_component_tags mct ON mpct.component_tag_id = mct.id + WHERE mpct.is_deleted = false + GROUP BY mpct.project_component_id +), + +related_projects AS ( + SELECT + pmp.project_id, + concat_ws(', '::text, pmp.project_id, string_agg(cmp.project_id::text, ', '::text)) AS related_project_ids_with_self, + concat_ws(', '::text, lpad(pmp.project_id::text, 5, '0'::text), string_agg(lpad(cmp.project_id::text, 5, '0'::text), ', '::text)) AS related_project_ids_searchable_with_self + FROM moped_project pmp + LEFT JOIN moped_project cmp ON pmp.project_id = cmp.parent_project_id + WHERE cmp.is_deleted = false + GROUP BY pmp.project_id +), + +latest_public_meeting_date AS ( + SELECT + mpm.project_id, + coalesce(max(mpm.date_actual), max(mpm.date_estimate)) AS latest + FROM moped_proj_milestones mpm + WHERE mpm.milestone_id = 65 AND mpm.is_deleted = false + GROUP BY mpm.project_id +), + +earliest_active_or_construction_phase_date AS ( + SELECT + mpp.project_id, + min(mpp.phase_start) AS earliest + FROM moped_proj_phases mpp + LEFT JOIN moped_phases mp ON mpp.phase_id = mp.phase_id + WHERE (mp.phase_name_simple = any(ARRAY['Active'::text, 'Construction'::text])) AND mpp.is_deleted = false + GROUP BY mpp.project_id +) + +SELECT + mpc.project_id, + comp_geography.project_component_id, + comp_geography.feature_ids, + mpc.component_id, + comp_geography.geometry, + comp_geography.line_geometry, + comp_geography.signal_ids, + council_districts.council_districts, + council_districts.council_districts_searchable, + NOT coalesce(council_districts.council_districts IS null OR council_districts.council_districts = ''::text, false) AS is_within_city_limits, + comp_geography.length_feet_total, + round(comp_geography.length_feet_total::numeric / 5280::numeric, 2) AS length_miles_total, + mc.component_name, + mc.component_subtype, + mc.component_name_full, + 'placeholder text'::text AS component_categories, + CASE + WHEN mc.line_representation = true THEN 'Line'::text + ELSE 'Point'::text + END AS geometry_type, + subcomponents.subcomponents AS component_subcomponents, + work_types.work_types AS component_work_types, + component_tags.component_tags, + mpc.description AS component_description, + mpc.interim_project_component_id, + coalesce(mpc.completion_date, plv.substantial_completion_date) AS substantial_completion_date, + plv.substantial_completion_date_estimated, + mpc.srts_id, + mpc.location_description AS component_location_description, + plv.project_name, + plv.project_name_secondary, + plv.project_name_full, + plv.project_description, + plv.ecapris_subproject_id, + plv.project_website, + plv.updated_at AS project_updated_at, + mpc.phase_id AS component_phase_id, + mph.phase_name AS component_phase_name, + mph.phase_name_simple AS component_phase_name_simple, + current_phase.phase_id AS project_phase_id, + current_phase.phase_name AS project_phase_name, + current_phase.phase_name_simple AS project_phase_name_simple, + coalesce(mph.phase_name, current_phase.phase_name) AS current_phase_name, + coalesce(mph.phase_name_simple, current_phase.phase_name_simple) AS current_phase_name_simple, + plv.project_team_members, + plv.project_sponsor, + plv.project_lead, + plv.public_process_status, + plv.interim_project_id, + plv.project_partners, + plv.task_order_names, + plv.funding_source_and_program_names AS funding_sources, + plv.type_name, + plv.project_status_update, + plv.project_status_update_date_created, + to_char(timezone('US/Central'::text, plv.construction_start_date), 'YYYY-MM-DD'::text) AS construction_start_date, + plv.project_inspector, + plv.project_designer, + plv.project_tags, + plv.workgroup_contractors, + plv.contract_numbers, + plv.parent_project_id, + plv.parent_project_name, + plv.parent_project_url, + plv.parent_project_name AS parent_project_name_full, + rp.related_project_ids_with_self AS related_project_ids, + rp.related_project_ids_searchable_with_self AS related_project_ids_searchable, + plv.knack_project_id AS knack_data_tracker_project_record_id, + plv.project_url, + (plv.project_url || '?tab=map&project_component_id='::text) || mpc.project_component_id::text AS component_url, + get_project_development_status(lpmd.latest::timestamp with time zone, eaocpd.earliest, coalesce(mpc.completion_date, plv.substantial_completion_date), plv.substantial_completion_date_estimated, coalesce(mph.phase_name_simple, current_phase.phase_name_simple)) AS project_development_status, + project_development_status_date.result AS project_development_status_date, + to_char(project_development_status_date.result, 'YYYY'::text)::integer AS project_development_status_date_calendar_year, + to_char(project_development_status_date.result, 'FMMonth YYYY'::text) AS project_development_status_date_calendar_year_month, + to_char(project_development_status_date.result, 'YYYY-MM'::text) AS project_development_status_date_calendar_year_month_numeric, + date_part('quarter'::text, project_development_status_date.result)::text AS project_development_status_date_calendar_year_quarter, + CASE + WHEN date_part('quarter'::text, project_development_status_date.result) = 4::double precision THEN (to_char(project_development_status_date.result, 'YYYY'::text)::integer + 1)::text + ELSE to_char(project_development_status_date.result, 'YYYY'::text) + END AS project_development_status_date_fiscal_year, + CASE + WHEN date_part('quarter'::text, project_development_status_date.result) = 4::double precision THEN 1::double precision + ELSE date_part('quarter'::text, project_development_status_date.result) + 1::double precision + END::text AS project_development_status_date_fiscal_year_quarter, + plv.added_by AS project_added_by +FROM moped_proj_components mpc +LEFT JOIN comp_geography ON mpc.project_component_id = comp_geography.project_component_id +LEFT JOIN council_districts ON mpc.project_component_id = council_districts.project_component_id +LEFT JOIN subcomponents ON mpc.project_component_id = subcomponents.project_component_id +LEFT JOIN work_types ON mpc.project_component_id = work_types.project_component_id +LEFT JOIN component_tags ON mpc.project_component_id = component_tags.project_component_id +LEFT JOIN project_list_view plv ON mpc.project_id = plv.project_id +LEFT JOIN current_phase_view current_phase ON mpc.project_id = current_phase.project_id +LEFT JOIN moped_phases mph ON mpc.phase_id = mph.phase_id +LEFT JOIN moped_components mc ON mpc.component_id = mc.component_id +LEFT JOIN related_projects rp ON mpc.project_id = rp.project_id +LEFT JOIN latest_public_meeting_date lpmd ON mpc.project_id = lpmd.project_id +LEFT JOIN earliest_active_or_construction_phase_date eaocpd ON mpc.project_id = eaocpd.project_id +LEFT JOIN LATERAL (SELECT timezone('US/Central'::text, get_project_development_status_date(lpmd.latest::timestamp with time zone, eaocpd.earliest, coalesce(mpc.completion_date, plv.substantial_completion_date), plv.substantial_completion_date_estimated, coalesce(mph.phase_name_simple, current_phase.phase_name_simple))) AS result) project_development_status_date ON true +WHERE mpc.is_deleted = false AND plv.is_deleted = false; + +CREATE OR REPLACE VIEW exploded_component_arcgis_online_view AS SELECT + component_arcgis_online_view.project_id, + component_arcgis_online_view.project_component_id, + st_geometrytype(dump.geom) AS geometry_type, + dump.path[1] AS point_index, + component_arcgis_online_view.geometry AS original_geometry, + st_asgeojson(dump.geom) AS exploded_geometry, + component_arcgis_online_view.project_updated_at +FROM component_arcgis_online_view, + LATERAL st_dump(st_geomfromgeojson(component_arcgis_online_view.geometry)) dump (path, geom) +WHERE st_geometrytype(st_geomfromgeojson(component_arcgis_online_view.geometry)) = 'ST_MultiPoint'::text; From 11438d27a8c4876276bd92f1003fffabe5cd921c Mon Sep 17 00:00:00 2001 From: Mike Date: Tue, 3 Dec 2024 17:28:20 -0600 Subject: [PATCH 57/85] Add CTE for project component work types --- .../1733184698645_add_comp_work_types/up.sql | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/moped-database/migrations/1733184698645_add_comp_work_types/up.sql b/moped-database/migrations/1733184698645_add_comp_work_types/up.sql index c650974aa0..0db50ff99e 100644 --- a/moped-database/migrations/1733184698645_add_comp_work_types/up.sql +++ b/moped-database/migrations/1733184698645_add_comp_work_types/up.sql @@ -167,6 +167,19 @@ min_estimated_phase_dates AS ( min(min_dates.min_date) AS min_phase_date FROM min_dates GROUP BY min_dates.project_id +), + +project_component_work_types AS ( + SELECT + mpc.project_id, + string_agg(DISTINCT mwt.name, ', '::text ORDER BY mwt.name) AS work_type_names + FROM moped_proj_components mpc + LEFT JOIN moped_proj_component_work_types mpcwt ON mpcwt.project_component_id = mpc.project_component_id + LEFT JOIN moped_work_types mwt ON mwt.id = mpcwt.work_type_id + WHERE TRUE + AND mpc.is_deleted = false + AND mpcwt.is_deleted = false + GROUP BY mpc.project_id ) SELECT @@ -254,7 +267,8 @@ SELECT concat(added_by_user.first_name, ' ', added_by_user.last_name) AS added_by, mpcs.components, districts.project_council_districts, - districts.project_and_child_project_council_districts + districts.project_and_child_project_council_districts, + pcwt.work_type_names FROM moped_project mp LEFT JOIN project_person_list_lookup ppll ON mp.project_id = ppll.project_id LEFT JOIN funding_sources_lookup fsl ON mp.project_id = fsl.project_id @@ -272,6 +286,7 @@ LEFT JOIN moped_proj_components_subtypes mpcs ON mp.project_id = mpcs.project_id LEFT JOIN project_district_association districts ON mp.project_id = districts.project_id LEFT JOIN min_confirmed_phase_dates mcpd ON mp.project_id = mcpd.project_id LEFT JOIN min_estimated_phase_dates mepd ON mp.project_id = mepd.project_id +LEFT JOIN project_component_work_types pcwt ON mp.project_id = pcwt.project_id LEFT JOIN LATERAL ( SELECT mpn.project_note, @@ -282,7 +297,7 @@ LEFT JOIN LATERAL ( LIMIT 1 ) proj_status_update ON true WHERE mp.is_deleted = false -GROUP BY mp.project_id, mp.project_name, mp.project_description, ppll.project_team_members, mp.ecapris_subproject_id, mp.date_added, mp.is_deleted, me.entity_name, mel.entity_name, mp.updated_at, mp.interim_project_id, mp.parent_project_id, mp.knack_project_id, current_phase.phase_name, current_phase.phase_key, current_phase.phase_name_simple, ptl.type_name, mpcs.components, fsl.funding_source_name, fsl.funding_program_names, fsl.funding_source_and_program_names, added_by_user.first_name, added_by_user.last_name, mpps.name, cpl.children_project_ids, proj_status_update.project_note, proj_status_update.date_created, work_activities.workgroup_contractors, work_activities.contract_numbers, work_activities.task_order_names, work_activities.task_order_names_short, work_activities.task_orders, districts.project_council_districts, districts.project_and_child_project_council_districts, mepd.min_phase_date, mcpd.min_phase_date; +GROUP BY mp.project_id, mp.project_name, mp.project_description, ppll.project_team_members, mp.ecapris_subproject_id, mp.date_added, mp.is_deleted, me.entity_name, mel.entity_name, mp.updated_at, mp.interim_project_id, mp.parent_project_id, mp.knack_project_id, current_phase.phase_name, current_phase.phase_key, current_phase.phase_name_simple, ptl.type_name, mpcs.components, fsl.funding_source_name, fsl.funding_program_names, fsl.funding_source_and_program_names, added_by_user.first_name, added_by_user.last_name, mpps.name, cpl.children_project_ids, proj_status_update.project_note, proj_status_update.date_created, work_activities.workgroup_contractors, work_activities.contract_numbers, work_activities.task_order_names, work_activities.task_order_names_short, work_activities.task_orders, districts.project_council_districts, districts.project_and_child_project_council_districts, mepd.min_phase_date, mcpd.min_phase_date, pcwt.work_type_names; CREATE OR REPLACE VIEW component_arcgis_online_view AS WITH work_types AS ( SELECT From c314ac097f532b474848924ecdc938bc6b271f94 Mon Sep 17 00:00:00 2001 From: Mike Date: Tue, 3 Dec 2024 17:33:59 -0600 Subject: [PATCH 58/85] Use a better name for the component work types column --- .../migrations/1733184698645_add_comp_work_types/up.sql | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/moped-database/migrations/1733184698645_add_comp_work_types/up.sql b/moped-database/migrations/1733184698645_add_comp_work_types/up.sql index 0db50ff99e..f04438c41f 100644 --- a/moped-database/migrations/1733184698645_add_comp_work_types/up.sql +++ b/moped-database/migrations/1733184698645_add_comp_work_types/up.sql @@ -172,7 +172,7 @@ min_estimated_phase_dates AS ( project_component_work_types AS ( SELECT mpc.project_id, - string_agg(DISTINCT mwt.name, ', '::text ORDER BY mwt.name) AS work_type_names + string_agg(DISTINCT mwt.name, ', '::text ORDER BY mwt.name) AS component_work_type_names FROM moped_proj_components mpc LEFT JOIN moped_proj_component_work_types mpcwt ON mpcwt.project_component_id = mpc.project_component_id LEFT JOIN moped_work_types mwt ON mwt.id = mpcwt.work_type_id @@ -268,7 +268,7 @@ SELECT mpcs.components, districts.project_council_districts, districts.project_and_child_project_council_districts, - pcwt.work_type_names + pcwt.component_work_type_names FROM moped_project mp LEFT JOIN project_person_list_lookup ppll ON mp.project_id = ppll.project_id LEFT JOIN funding_sources_lookup fsl ON mp.project_id = fsl.project_id @@ -297,7 +297,7 @@ LEFT JOIN LATERAL ( LIMIT 1 ) proj_status_update ON true WHERE mp.is_deleted = false -GROUP BY mp.project_id, mp.project_name, mp.project_description, ppll.project_team_members, mp.ecapris_subproject_id, mp.date_added, mp.is_deleted, me.entity_name, mel.entity_name, mp.updated_at, mp.interim_project_id, mp.parent_project_id, mp.knack_project_id, current_phase.phase_name, current_phase.phase_key, current_phase.phase_name_simple, ptl.type_name, mpcs.components, fsl.funding_source_name, fsl.funding_program_names, fsl.funding_source_and_program_names, added_by_user.first_name, added_by_user.last_name, mpps.name, cpl.children_project_ids, proj_status_update.project_note, proj_status_update.date_created, work_activities.workgroup_contractors, work_activities.contract_numbers, work_activities.task_order_names, work_activities.task_order_names_short, work_activities.task_orders, districts.project_council_districts, districts.project_and_child_project_council_districts, mepd.min_phase_date, mcpd.min_phase_date, pcwt.work_type_names; +GROUP BY mp.project_id, mp.project_name, mp.project_description, ppll.project_team_members, mp.ecapris_subproject_id, mp.date_added, mp.is_deleted, me.entity_name, mel.entity_name, mp.updated_at, mp.interim_project_id, mp.parent_project_id, mp.knack_project_id, current_phase.phase_name, current_phase.phase_key, current_phase.phase_name_simple, ptl.type_name, mpcs.components, fsl.funding_source_name, fsl.funding_program_names, fsl.funding_source_and_program_names, added_by_user.first_name, added_by_user.last_name, mpps.name, cpl.children_project_ids, proj_status_update.project_note, proj_status_update.date_created, work_activities.workgroup_contractors, work_activities.contract_numbers, work_activities.task_order_names, work_activities.task_order_names_short, work_activities.task_orders, districts.project_council_districts, districts.project_and_child_project_council_districts, mepd.min_phase_date, mcpd.min_phase_date, pcwt.component_work_type_names; CREATE OR REPLACE VIEW component_arcgis_online_view AS WITH work_types AS ( SELECT From 9fbc7c3f009b300b6f7a51754878f8cd185534c4 Mon Sep 17 00:00:00 2001 From: Moped View Bot Date: Tue, 3 Dec 2024 23:35:31 +0000 Subject: [PATCH 59/85] =?UTF-8?q?=F0=9F=A4=96=20Export=20view=20for=202001?= =?UTF-8?q?6=5Fcomponent=5Fwork=5Ftypes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../views/component_arcgis_online_view.sql | 2 +- .../exploded_component_arcgis_online_view.sql | 2 +- moped-database/views/project_list_view.sql | 19 ++++++++++++++++--- 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/moped-database/views/component_arcgis_online_view.sql b/moped-database/views/component_arcgis_online_view.sql index df627039ef..ea16e61eeb 100644 --- a/moped-database/views/component_arcgis_online_view.sql +++ b/moped-database/views/component_arcgis_online_view.sql @@ -1,4 +1,4 @@ --- Most recent migration: moped-database/migrations/1730931541883_update_agol_view_geometry/up.sql +-- Most recent migration: moped-database/migrations/1733184698645_add_comp_work_types/up.sql CREATE OR REPLACE VIEW component_arcgis_online_view AS WITH work_types AS ( SELECT diff --git a/moped-database/views/exploded_component_arcgis_online_view.sql b/moped-database/views/exploded_component_arcgis_online_view.sql index 12451248e3..32459065a2 100644 --- a/moped-database/views/exploded_component_arcgis_online_view.sql +++ b/moped-database/views/exploded_component_arcgis_online_view.sql @@ -1,4 +1,4 @@ --- Most recent migration: moped-database/migrations/1730931541883_update_agol_view_geometry/up.sql +-- Most recent migration: moped-database/migrations/1733184698645_add_comp_work_types/up.sql CREATE OR REPLACE VIEW exploded_component_arcgis_online_view AS SELECT component_arcgis_online_view.project_id, diff --git a/moped-database/views/project_list_view.sql b/moped-database/views/project_list_view.sql index 7c81ae2ff9..86e3824226 100644 --- a/moped-database/views/project_list_view.sql +++ b/moped-database/views/project_list_view.sql @@ -1,4 +1,4 @@ --- Most recent migration: moped-database/migrations/1729197757693_remove_completion_date_from_project_list_view/up.sql +-- Most recent migration: moped-database/migrations/1733184698645_add_comp_work_types/up.sql CREATE OR REPLACE VIEW project_list_view AS WITH project_person_list_lookup AS ( SELECT @@ -165,6 +165,17 @@ min_estimated_phase_dates AS ( min(min_dates.min_date) AS min_phase_date FROM min_dates GROUP BY min_dates.project_id +), + +project_component_work_types AS ( + SELECT + mpc.project_id, + string_agg(DISTINCT mwt.name, ', '::text ORDER BY mwt.name) AS component_work_type_names + FROM moped_proj_components mpc + LEFT JOIN moped_proj_component_work_types mpcwt ON mpcwt.project_component_id = mpc.project_component_id + LEFT JOIN moped_work_types mwt ON mwt.id = mpcwt.work_type_id + WHERE true AND mpc.is_deleted = false AND mpcwt.is_deleted = false + GROUP BY mpc.project_id ) SELECT @@ -252,7 +263,8 @@ SELECT concat(added_by_user.first_name, ' ', added_by_user.last_name) AS added_by, mpcs.components, districts.project_council_districts, - districts.project_and_child_project_council_districts + districts.project_and_child_project_council_districts, + pcwt.component_work_type_names FROM moped_project mp LEFT JOIN project_person_list_lookup ppll ON mp.project_id = ppll.project_id LEFT JOIN funding_sources_lookup fsl ON mp.project_id = fsl.project_id @@ -270,6 +282,7 @@ LEFT JOIN moped_proj_components_subtypes mpcs ON mp.project_id = mpcs.project_id LEFT JOIN project_district_association districts ON mp.project_id = districts.project_id LEFT JOIN min_confirmed_phase_dates mcpd ON mp.project_id = mcpd.project_id LEFT JOIN min_estimated_phase_dates mepd ON mp.project_id = mepd.project_id +LEFT JOIN project_component_work_types pcwt ON mp.project_id = pcwt.project_id LEFT JOIN LATERAL ( SELECT mpn.project_note, @@ -280,4 +293,4 @@ LEFT JOIN LATERAL ( LIMIT 1 ) proj_status_update ON true WHERE mp.is_deleted = false -GROUP BY mp.project_id, mp.project_name, mp.project_description, ppll.project_team_members, mp.ecapris_subproject_id, mp.date_added, mp.is_deleted, me.entity_name, mel.entity_name, mp.updated_at, mp.interim_project_id, mp.parent_project_id, mp.knack_project_id, current_phase.phase_name, current_phase.phase_key, current_phase.phase_name_simple, ptl.type_name, mpcs.components, fsl.funding_source_name, fsl.funding_program_names, fsl.funding_source_and_program_names, added_by_user.first_name, added_by_user.last_name, mpps.name, cpl.children_project_ids, proj_status_update.project_note, proj_status_update.date_created, work_activities.workgroup_contractors, work_activities.contract_numbers, work_activities.task_order_names, work_activities.task_order_names_short, work_activities.task_orders, districts.project_council_districts, districts.project_and_child_project_council_districts, mepd.min_phase_date, mcpd.min_phase_date; +GROUP BY mp.project_id, mp.project_name, mp.project_description, ppll.project_team_members, mp.ecapris_subproject_id, mp.date_added, mp.is_deleted, me.entity_name, mel.entity_name, mp.updated_at, mp.interim_project_id, mp.parent_project_id, mp.knack_project_id, current_phase.phase_name, current_phase.phase_key, current_phase.phase_name_simple, ptl.type_name, mpcs.components, fsl.funding_source_name, fsl.funding_program_names, fsl.funding_source_and_program_names, added_by_user.first_name, added_by_user.last_name, mpps.name, cpl.children_project_ids, proj_status_update.project_note, proj_status_update.date_created, work_activities.workgroup_contractors, work_activities.contract_numbers, work_activities.task_order_names, work_activities.task_order_names_short, work_activities.task_orders, districts.project_council_districts, districts.project_and_child_project_council_districts, mepd.min_phase_date, mcpd.min_phase_date, pcwt.component_work_type_names; From f352b4aebca2910dc6249258af1e75686bb407f6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 4 Dec 2024 23:58:18 +0000 Subject: [PATCH 60/85] Bump http-proxy-middleware from 2.0.6 to 2.0.7 in /moped-editor Bumps [http-proxy-middleware](https://github.com/chimurai/http-proxy-middleware) from 2.0.6 to 2.0.7. - [Release notes](https://github.com/chimurai/http-proxy-middleware/releases) - [Changelog](https://github.com/chimurai/http-proxy-middleware/blob/v2.0.7/CHANGELOG.md) - [Commits](https://github.com/chimurai/http-proxy-middleware/compare/v2.0.6...v2.0.7) --- updated-dependencies: - dependency-name: http-proxy-middleware dependency-type: indirect ... Signed-off-by: dependabot[bot] --- moped-editor/package-lock.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/moped-editor/package-lock.json b/moped-editor/package-lock.json index e9fefc13a0..82f1109718 100644 --- a/moped-editor/package-lock.json +++ b/moped-editor/package-lock.json @@ -1,12 +1,12 @@ { "name": "atd-moped-editor", - "version": "2.23.0", + "version": "2.24.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "atd-moped-editor", - "version": "2.23.0", + "version": "2.24.0", "hasInstallScript": true, "license": "CC0-1.0", "dependencies": { @@ -21643,9 +21643,9 @@ } }, "node_modules/http-proxy-middleware": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz", - "integrity": "sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw==", + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.7.tgz", + "integrity": "sha512-fgVY8AV7qU7z/MmXJ/rxwbrtQH4jBQ9m7kp3llF0liB7glmFeVZFBepQb32T3y8n8k2+AEYuMPCpinYW+/CuRA==", "dependencies": { "@types/http-proxy": "^1.17.8", "http-proxy": "^1.18.1", From c2fc86c6709026a6013e72c1d31e272f3c97c348 Mon Sep 17 00:00:00 2001 From: Mike Date: Thu, 5 Dec 2024 11:21:23 -0600 Subject: [PATCH 61/85] Fix typo in department org fk constraint --- moped-database/migrations/1732661952305_department_reorg/up.sql | 2 +- moped-etl/README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/moped-database/migrations/1732661952305_department_reorg/up.sql b/moped-database/migrations/1732661952305_department_reorg/up.sql index 55edb0c7b5..4ae3b9d1d9 100644 --- a/moped-database/migrations/1732661952305_department_reorg/up.sql +++ b/moped-database/migrations/1732661952305_department_reorg/up.sql @@ -80,7 +80,7 @@ ADD CONSTRAINT moped_entity_organization_id_fkey FOREIGN KEY ("organization_id") REFERENCES moped_organization ("organization_id"); ALTER TABLE moped_department -ADD CONSTRAINT moped_entity_organization_id_fkey FOREIGN KEY ("organization_id") +ADD CONSTRAINT moped_department_organization_id_fkey FOREIGN KEY ("organization_id") REFERENCES moped_organization ("organization_id"); -- Remove row with 0 for id and 'None' for name; we're not using it and null is more appropriate for entities and workgroups with no department diff --git a/moped-etl/README.md b/moped-etl/README.md index fdfdb4c908..d71ea0c683 100644 --- a/moped-etl/README.md +++ b/moped-etl/README.md @@ -1,3 +1,3 @@ # Moped ETLs -These ETLs are scheduled through our [Airflow](https://github.com/cityofaustin/atd-airflow) deployment. Each folder contains a containerized Python script and the Docker images are updated through Github Action workflows. +These ETLs are scheduled through our [Airflow](https://github.com/cityofaustin/atd-airflow) deployment. Each folder contains a containerized Python script and the Docker images are updated through GitHub Action workflows. From 108eb4bfe7a4672787e0cb1d7e1ff5500e03dc2e Mon Sep 17 00:00:00 2001 From: Mike Date: Thu, 5 Dec 2024 13:01:01 -0600 Subject: [PATCH 62/85] Bump version --- moped-editor/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/moped-editor/package.json b/moped-editor/package.json index e6ecd00d02..e94f10e23f 100644 --- a/moped-editor/package.json +++ b/moped-editor/package.json @@ -2,7 +2,7 @@ "name": "atd-moped-editor", "author": "ATD Data & Technology Services", "license": "CC0-1.0", - "version": "2.24.0", + "version": "2.25.0", "homepage": "/moped", "private": false, "repository": { From e9ac0f59e08efdd95eab30741b081ec28258cffb Mon Sep 17 00:00:00 2001 From: Frank Hereford Date: Thu, 5 Dec 2024 14:08:44 -0600 Subject: [PATCH 63/85] bump local graphql-engine version --- moped-database/docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/moped-database/docker-compose.yml b/moped-database/docker-compose.yml index 894fe1236b..e276799246 100644 --- a/moped-database/docker-compose.yml +++ b/moped-database/docker-compose.yml @@ -1,7 +1,7 @@ # Architecture Independent Docker Stack Components for Moped Development Environment services: hasura: - image: hasura/graphql-engine:v2.44.0 + image: hasura/graphql-engine:v2.45.0 depends_on: - moped-pgsql expose: From ace1a137db668f3494595e1eab98601ed44e8fd6 Mon Sep 17 00:00:00 2001 From: rose Date: Mon, 9 Dec 2024 23:30:24 -0600 Subject: [PATCH 64/85] create reuseable component for datagrid tables that contains the action buttons and save is disabled until required fields are present --- .../src/components/DataGridActions.js | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 moped-editor/src/components/DataGridActions.js diff --git a/moped-editor/src/components/DataGridActions.js b/moped-editor/src/components/DataGridActions.js new file mode 100644 index 0000000000..5d4dfd1f41 --- /dev/null +++ b/moped-editor/src/components/DataGridActions.js @@ -0,0 +1,59 @@ +import { + GridRowModes, + GridActionsCellItem, + useGridApiContext, + useGridSelector, +} from "@mui/x-data-grid-pro"; +import CheckIcon from "@mui/icons-material/Check"; +import CloseIcon from "@mui/icons-material/Close"; +import { DeleteOutline as DeleteOutlineIcon } from "@mui/icons-material"; + +import { defaultEditColumnIconStyle } from "src/utils/dataGridHelpers"; + +const DataGridActions = ({ + id, + requiredField, + rowModesModel, + handleCancelClick, + handleDeleteOpen, + handleSaveClick, +}) => { + const isInEditMode = rowModesModel[id]?.mode === GridRowModes.Edit; + + const apiRef = useGridApiContext(); + + const hasRequiredField = useGridSelector(apiRef, () => { + const editState = apiRef.current.state.editRows; + return !!editState[id]?.[requiredField]?.value; + }); + if (isInEditMode) { + return [ + } + label="Save" + sx={{ + color: "primary.main", + }} + onClick={handleSaveClick(id)} + disabled={!hasRequiredField} + />, + } + label="Cancel" + className="textPrimary" + onClick={handleCancelClick(id)} + color="inherit" + />, + ]; + } + return [ + } + label="Delete" + onClick={() => handleDeleteOpen(id)} + color="inherit" + />, + ]; +}; + +export default DataGridActions; From 1c19f1a0eb9b8825ae064179dc587bd90d8de37e Mon Sep 17 00:00:00 2001 From: rose Date: Mon, 9 Dec 2024 23:30:35 -0600 Subject: [PATCH 65/85] use reuseable datagridactions component --- .../ProjectSummary/SubprojectsTable.js | 51 +++++-------------- 1 file changed, 12 insertions(+), 39 deletions(-) diff --git a/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectsTable.js b/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectsTable.js index ad6078241e..3a29a67042 100644 --- a/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectsTable.js +++ b/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectsTable.js @@ -1,25 +1,18 @@ import React, { useState, useEffect, useMemo, useCallback } from "react"; import { useQuery, useMutation } from "@apollo/client"; -import CheckIcon from "@mui/icons-material/Check"; -import CloseIcon from "@mui/icons-material/Close"; import { CircularProgress } from "@mui/material"; -import { DeleteOutline as DeleteOutlineIcon } from "@mui/icons-material"; -import { - DataGridPro, - GridRowModes, - GridActionsCellItem, -} from "@mui/x-data-grid-pro"; +import { DataGridPro, GridRowModes } from "@mui/x-data-grid-pro"; import { v4 as uuidv4 } from "uuid"; import dataGridProStyleOverrides from "src/styles/dataGridProStylesOverrides"; import SubprojectsToolbar from "./SubprojectsToolbar"; +import DataGridActions from "src/components/DataGridActions"; import ApolloErrorHandler from "../../../../components/ApolloErrorHandler"; import ProjectStatusBadge from "../../projectView/ProjectStatusBadge"; import SubprojectLookupComponent from "./SubprojectLookupComponent"; import DeleteConfirmationModal from "../DeleteConfirmationModal"; import RenderFieldLink from "src/components/RenderFieldLink"; -import { defaultEditColumnIconStyle } from "src/utils/dataGridHelpers"; import { SUBPROJECT_QUERY, @@ -86,36 +79,16 @@ const useColumns = ({ sortable: false, editable: false, type: "actions", - getActions: ({ id }) => { - const isInEditMode = rowModesModel[id]?.mode === GridRowModes.Edit; - if (isInEditMode) { - return [ - } - label="Save" - sx={{ - color: "primary.main", - }} - onClick={handleSaveClick(id)} - />, - } - label="Cancel" - className="textPrimary" - onClick={handleCancelClick(id)} - color="inherit" - />, - ]; - } - return [ - } - label="Delete" - onClick={() => handleDeleteOpen(id)} - color="inherit" - />, - ]; - }, + renderCell: ({ id }) => ( + + ), }, ]; }, [ From cdbd675dff25b4b2f39f55bf640a9edb3ed3d075 Mon Sep 17 00:00:00 2001 From: rose Date: Tue, 10 Dec 2024 16:56:26 -0600 Subject: [PATCH 66/85] add optional edit button to datagridactions component --- .../src/components/DataGridActions.js | 28 +++++++++++++++---- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/moped-editor/src/components/DataGridActions.js b/moped-editor/src/components/DataGridActions.js index 5d4dfd1f41..3e128d5470 100644 --- a/moped-editor/src/components/DataGridActions.js +++ b/moped-editor/src/components/DataGridActions.js @@ -6,25 +6,34 @@ import { } from "@mui/x-data-grid-pro"; import CheckIcon from "@mui/icons-material/Check"; import CloseIcon from "@mui/icons-material/Close"; -import { DeleteOutline as DeleteOutlineIcon } from "@mui/icons-material"; +import { + EditOutlined as EditOutlinedIcon, + DeleteOutline as DeleteOutlineIcon, +} from "@mui/icons-material"; import { defaultEditColumnIconStyle } from "src/utils/dataGridHelpers"; const DataGridActions = ({ id, - requiredField, + requiredFields, rowModesModel, handleCancelClick, handleDeleteOpen, handleSaveClick, + handleEditClick, }) => { const isInEditMode = rowModesModel[id]?.mode === GridRowModes.Edit; const apiRef = useGridApiContext(); - const hasRequiredField = useGridSelector(apiRef, () => { + const hasRequiredFields = useGridSelector(apiRef, () => { const editState = apiRef.current.state.editRows; - return !!editState[id]?.[requiredField]?.value; + for (const field of requiredFields) { + if (!editState[id]?.[field]?.value) { + return false; + } + } + return true; }); if (isInEditMode) { return [ @@ -35,7 +44,7 @@ const DataGridActions = ({ color: "primary.main", }} onClick={handleSaveClick(id)} - disabled={!hasRequiredField} + disabled={!hasRequiredFields} />, } @@ -47,6 +56,15 @@ const DataGridActions = ({ ]; } return [ + handleEditClick && ( + } + label="Edit" + className="textPrimary" + onClick={handleEditClick(id)} + color="inherit" + /> + ), } label="Delete" From 2ad3387c38e006591daa9fc95b75b2f85ad7747d Mon Sep 17 00:00:00 2001 From: rose Date: Tue, 10 Dec 2024 16:56:49 -0600 Subject: [PATCH 67/85] make milestones table use actions component that disables save --- .../projects/projectView/ProjectMilestones.js | 63 ++++--------------- 1 file changed, 13 insertions(+), 50 deletions(-) diff --git a/moped-editor/src/views/projects/projectView/ProjectMilestones.js b/moped-editor/src/views/projects/projectView/ProjectMilestones.js index 6411fd3642..821d1265b1 100644 --- a/moped-editor/src/views/projects/projectView/ProjectMilestones.js +++ b/moped-editor/src/views/projects/projectView/ProjectMilestones.js @@ -3,20 +3,8 @@ import isEqual from "lodash/isEqual"; import { v4 as uuidv4 } from "uuid"; import { CircularProgress } from "@mui/material"; -import { - EditOutlined as EditOutlinedIcon, - DeleteOutline as DeleteOutlineIcon, -} from "@mui/icons-material"; -import CheckIcon from "@mui/icons-material/Check"; -import CloseIcon from "@mui/icons-material/Close"; -import { - DataGridPro, - GridRowModes, - GridActionsCellItem, - useGridApiRef, -} from "@mui/x-data-grid-pro"; +import { DataGridPro, GridRowModes, useGridApiRef } from "@mui/x-data-grid-pro"; import dataGridProStyleOverrides from "src/styles/dataGridProStylesOverrides"; -import { defaultEditColumnIconStyle } from "src/utils/dataGridHelpers"; import ProjectMilestoneToolbar from "./ProjectMilestones/ProjectMilestoneToolbar"; import DataGridTextField from "./DataGridTextField"; import RelatedPhaseTextField from "./ProjectMilestones/RelatedPhaseTextField"; @@ -36,6 +24,7 @@ import MilestoneTemplateModal from "./ProjectMilestones/MilestoneTemplateModal"; import MilestoneAutocompleteComponent from "./ProjectMilestones/MilestoneAutocompleteComponent"; import DataGridDateFieldEdit from "./ProjectMilestones/DataGridDateFieldEdit"; import DeleteConfirmationModal from "./DeleteConfirmationModal"; +import DataGridActions from "src/components/DataGridActions"; const useMilestoneNameLookup = (data) => useMemo(() => { @@ -167,43 +156,17 @@ const useColumns = ({ sortable: false, editable: false, type: "actions", - getActions: ({ id }) => { - const isInEditMode = rowModesModel[id]?.mode === GridRowModes.Edit; - if (isInEditMode) { - return [ - } - label="Save" - sx={{ - color: "primary.main", - }} - onClick={handleSaveClick(id)} - />, - } - label="Cancel" - className="textPrimary" - onClick={handleCancelClick(id)} - color="inherit" - />, - ]; - } - return [ - } - label="Edit" - className="textPrimary" - onClick={handleEditClick(id)} - color="inherit" - />, - } - label="Delete" - onClick={() => handleDeleteOpen(id)} - color="inherit" - />, - ]; - }, + renderCell: ({ id }) => ( + + ), }, ]; }, [ From c2f08e01e7f58db066ce75340cc16e569658d83d Mon Sep 17 00:00:00 2001 From: rose Date: Tue, 10 Dec 2024 16:57:17 -0600 Subject: [PATCH 68/85] clean up closing tag --- .../projects/projectView/ProjectSummary/SubprojectsTable.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectsTable.js b/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectsTable.js index 3a29a67042..2bf0cfdc9a 100644 --- a/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectsTable.js +++ b/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectsTable.js @@ -82,12 +82,12 @@ const useColumns = ({ renderCell: ({ id }) => ( + /> ), }, ]; From cad6b143c6e00b010536a3b7011f5ce55286825f Mon Sep 17 00:00:00 2001 From: rose Date: Wed, 11 Dec 2024 16:07:52 -0600 Subject: [PATCH 69/85] add comments --- .../components/DataGridPro/DataGridActions.js | 91 +++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 moped-editor/src/components/DataGridPro/DataGridActions.js diff --git a/moped-editor/src/components/DataGridPro/DataGridActions.js b/moped-editor/src/components/DataGridPro/DataGridActions.js new file mode 100644 index 0000000000..8ceb6dca44 --- /dev/null +++ b/moped-editor/src/components/DataGridPro/DataGridActions.js @@ -0,0 +1,91 @@ +import { + GridRowModes, + GridActionsCellItem, + useGridApiContext, + useGridSelector, +} from "@mui/x-data-grid-pro"; +import CheckIcon from "@mui/icons-material/Check"; +import CloseIcon from "@mui/icons-material/Close"; +import { + EditOutlined as EditOutlinedIcon, + DeleteOutline as DeleteOutlineIcon, +} from "@mui/icons-material"; + +import { defaultEditColumnIconStyle } from "src/utils/dataGridHelpers"; + +/** Component for Data Grid table action buttons + * @param {Integer} id - Data Grid row id (same as project id) + * @param {Array} requiredFields - fields that are required in order to save row + * @param {Object} rowModesModel - row modes state from data grid + * @param {Function} handleCancelClick - handles cancel button click + * @param {Function} handleDeleteClick - handles delete button click + * @param {Function} handleSaveClick - handles save button click + * @param {Function} handleEditClick - handles edit button click, optional + * @return {JSX.Element} + */ + +const DataGridActions = ({ + id, + requiredFields, + rowModesModel, + handleCancelClick, + handleDeleteOpen, + handleSaveClick, + handleEditClick, +}) => { + const isInEditMode = rowModesModel[id]?.mode === GridRowModes.Edit; + + const apiRef = useGridApiContext(); + + // check if all the required fields are filled out + const hasRequiredFields = useGridSelector(apiRef, () => { + const editState = apiRef.current.state.editRows; + for (const field of requiredFields) { + if (!editState[id]?.[field]?.value) { + return false; + } + } + return true; + }); + + if (isInEditMode) { + return [ + } + label="Save" + sx={{ + color: "primary.main", + }} + onClick={handleSaveClick(id)} + disabled={!hasRequiredFields} + />, + } + label="Cancel" + className="textPrimary" + onClick={handleCancelClick(id)} + color="inherit" + />, + ]; + } + return [ + // only render edit button if we were passed an edit handler and are not currently in edit mode + handleEditClick && ( + } + label="Edit" + className="textPrimary" + onClick={handleEditClick(id)} + color="inherit" + /> + ), + } + label="Delete" + onClick={() => handleDeleteOpen(id)} + color="inherit" + />, + ]; +}; + +export default DataGridActions; From 606488137f170451bf80c0d8c8febbf7f16c9b93 Mon Sep 17 00:00:00 2001 From: rose Date: Wed, 11 Dec 2024 16:08:08 -0600 Subject: [PATCH 70/85] move file --- .../src/components/DataGridActions.js | 77 ------------------- 1 file changed, 77 deletions(-) delete mode 100644 moped-editor/src/components/DataGridActions.js diff --git a/moped-editor/src/components/DataGridActions.js b/moped-editor/src/components/DataGridActions.js deleted file mode 100644 index 3e128d5470..0000000000 --- a/moped-editor/src/components/DataGridActions.js +++ /dev/null @@ -1,77 +0,0 @@ -import { - GridRowModes, - GridActionsCellItem, - useGridApiContext, - useGridSelector, -} from "@mui/x-data-grid-pro"; -import CheckIcon from "@mui/icons-material/Check"; -import CloseIcon from "@mui/icons-material/Close"; -import { - EditOutlined as EditOutlinedIcon, - DeleteOutline as DeleteOutlineIcon, -} from "@mui/icons-material"; - -import { defaultEditColumnIconStyle } from "src/utils/dataGridHelpers"; - -const DataGridActions = ({ - id, - requiredFields, - rowModesModel, - handleCancelClick, - handleDeleteOpen, - handleSaveClick, - handleEditClick, -}) => { - const isInEditMode = rowModesModel[id]?.mode === GridRowModes.Edit; - - const apiRef = useGridApiContext(); - - const hasRequiredFields = useGridSelector(apiRef, () => { - const editState = apiRef.current.state.editRows; - for (const field of requiredFields) { - if (!editState[id]?.[field]?.value) { - return false; - } - } - return true; - }); - if (isInEditMode) { - return [ - } - label="Save" - sx={{ - color: "primary.main", - }} - onClick={handleSaveClick(id)} - disabled={!hasRequiredFields} - />, - } - label="Cancel" - className="textPrimary" - onClick={handleCancelClick(id)} - color="inherit" - />, - ]; - } - return [ - handleEditClick && ( - } - label="Edit" - className="textPrimary" - onClick={handleEditClick(id)} - color="inherit" - /> - ), - } - label="Delete" - onClick={() => handleDeleteOpen(id)} - color="inherit" - />, - ]; -}; - -export default DataGridActions; From 9b069c17d8c20bc898f594588ea286261d799eca Mon Sep 17 00:00:00 2001 From: rose Date: Wed, 11 Dec 2024 16:09:17 -0600 Subject: [PATCH 71/85] use new actions component --- .../projects/projectView/ProjectFiles.js | 57 ++++--------------- .../ProjectFunding/ProjectFundingTable.js | 57 ++++--------------- .../projects/projectView/ProjectMilestones.js | 2 +- 3 files changed, 25 insertions(+), 91 deletions(-) diff --git a/moped-editor/src/views/projects/projectView/ProjectFiles.js b/moped-editor/src/views/projects/projectView/ProjectFiles.js index 145e76f6f6..8ffff29c58 100644 --- a/moped-editor/src/views/projects/projectView/ProjectFiles.js +++ b/moped-editor/src/views/projects/projectView/ProjectFiles.js @@ -5,21 +5,13 @@ import isEqual from "lodash/isEqual"; import { CardContent, CircularProgress, Link, Typography } from "@mui/material"; import makeStyles from "@mui/styles/makeStyles"; -import { - DeleteOutline as DeleteOutlineIcon, - EditOutlined as EditOutlinedIcon, -} from "@mui/icons-material"; -import CheckIcon from "@mui/icons-material/Check"; -import CloseIcon from "@mui/icons-material/Close"; import { DataGridPro, GridRowModes, - GridActionsCellItem, useGridApiRef, gridStringOrNumberComparator, } from "@mui/x-data-grid-pro"; import dataGridProStyleOverrides from "src/styles/dataGridProStylesOverrides"; -import { defaultEditColumnIconStyle } from "src/utils/dataGridHelpers"; import { useMutation, useQuery } from "@apollo/client"; import humanReadableFileSize from "../../../utils/humanReadableFileSize"; @@ -40,6 +32,7 @@ import ProjectFilesToolbar from "./ProjectFilesToolbar"; import DataGridTextField from "./DataGridTextField"; import ProjectFilesTypeSelect from "./ProjectFilesTypeSelect"; import DeleteConfirmationModal from "./DeleteConfirmationModal"; +import DataGridActions from "src/components/DataGridPro/DataGridActions"; const useStyles = makeStyles(() => ({ title: { @@ -212,43 +205,17 @@ const useColumns = ({ sortable: false, editable: false, type: "actions", - getActions: ({ id }) => { - const isInEditMode = rowModesModel[id]?.mode === GridRowModes.Edit; - if (isInEditMode) { - return [ - } - label="Save" - sx={{ - color: "primary.main", - }} - onClick={handleSaveClick(id)} - />, - } - label="Cancel" - className="textPrimary" - onClick={handleCancelClick(id)} - color="inherit" - />, - ]; - } - return [ - } - label="Edit" - className="textPrimary" - onClick={handleEditClick(id)} - color="inherit" - />, - } - label="Delete" - onClick={() => handleDeleteOpen(id)} - color="inherit" - />, - ]; - }, + renderCell: ({ id }) => ( + + ), }, ]; }, [ diff --git a/moped-editor/src/views/projects/projectView/ProjectFunding/ProjectFundingTable.js b/moped-editor/src/views/projects/projectView/ProjectFunding/ProjectFundingTable.js index ba7a0139e6..f0b34ea4d3 100644 --- a/moped-editor/src/views/projects/projectView/ProjectFunding/ProjectFundingTable.js +++ b/moped-editor/src/views/projects/projectView/ProjectFunding/ProjectFundingTable.js @@ -5,17 +5,10 @@ import isEqual from "lodash/isEqual"; // Material import { Alert, CircularProgress, Snackbar, Box } from "@mui/material"; -import { - DeleteOutline as DeleteOutlineIcon, - EditOutlined as EditOutlinedIcon, -} from "@mui/icons-material"; -import CheckIcon from "@mui/icons-material/Check"; -import CloseIcon from "@mui/icons-material/Close"; import makeStyles from "@mui/styles/makeStyles"; import { DataGridPro, GridRowModes, - GridActionsCellItem, useGridApiRef, gridColumnFieldsSelector, } from "@mui/x-data-grid-pro"; @@ -43,7 +36,7 @@ import FundAutocompleteComponent from "./FundAutocompleteComponent"; import dataGridProStyleOverrides from "src/styles/dataGridProStylesOverrides"; import DeleteConfirmationModal from "../DeleteConfirmationModal"; import { getLookupValueByID } from "./utils/helpers"; -import { defaultEditColumnIconStyle } from "src/utils/dataGridHelpers"; +import DataGridActions from "src/components/DataGridPro/DataGridActions"; const useStyles = makeStyles((theme) => ({ fieldGridItem: { @@ -227,43 +220,17 @@ const useColumns = ({ sortable: false, editable: false, type: "actions", - getActions: ({ id }) => { - const isInEditMode = rowModesModel[id]?.mode === GridRowModes.Edit; - if (isInEditMode) { - return [ - } - label="Save" - sx={{ - color: "primary.main", - }} - onClick={handleSaveClick(id)} - />, - } - label="Cancel" - className="textPrimary" - onClick={handleCancelClick(id)} - color="inherit" - />, - ]; - } - return [ - } - label="Edit" - className="textPrimary" - onClick={handleEditClick(id)} - color="inherit" - />, - } - label="Delete" - onClick={() => handleDeleteOpen(id)} - color="inherit" - />, - ]; - }, + renderCell: ({ id }) => ( + + ), }, ]; }, [ diff --git a/moped-editor/src/views/projects/projectView/ProjectMilestones.js b/moped-editor/src/views/projects/projectView/ProjectMilestones.js index 821d1265b1..74bd2d8766 100644 --- a/moped-editor/src/views/projects/projectView/ProjectMilestones.js +++ b/moped-editor/src/views/projects/projectView/ProjectMilestones.js @@ -24,7 +24,7 @@ import MilestoneTemplateModal from "./ProjectMilestones/MilestoneTemplateModal"; import MilestoneAutocompleteComponent from "./ProjectMilestones/MilestoneAutocompleteComponent"; import DataGridDateFieldEdit from "./ProjectMilestones/DataGridDateFieldEdit"; import DeleteConfirmationModal from "./DeleteConfirmationModal"; -import DataGridActions from "src/components/DataGridActions"; +import DataGridActions from "src/components/DataGridPro/DataGridActions"; const useMilestoneNameLookup = (data) => useMemo(() => { From 35776973261c95cf88ff900b3de7ca2df1d00b11 Mon Sep 17 00:00:00 2001 From: rose Date: Wed, 11 Dec 2024 16:09:33 -0600 Subject: [PATCH 72/85] change order of import --- .../projects/projectView/ProjectSummary/SubprojectsTable.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectsTable.js b/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectsTable.js index 2bf0cfdc9a..816c69e6a1 100644 --- a/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectsTable.js +++ b/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectsTable.js @@ -5,9 +5,9 @@ import { CircularProgress } from "@mui/material"; import { DataGridPro, GridRowModes } from "@mui/x-data-grid-pro"; import { v4 as uuidv4 } from "uuid"; +import DataGridActions from "src/components/DataGridPro/DataGridActions"; import dataGridProStyleOverrides from "src/styles/dataGridProStylesOverrides"; import SubprojectsToolbar from "./SubprojectsToolbar"; -import DataGridActions from "src/components/DataGridActions"; import ApolloErrorHandler from "../../../../components/ApolloErrorHandler"; import ProjectStatusBadge from "../../projectView/ProjectStatusBadge"; import SubprojectLookupComponent from "./SubprojectLookupComponent"; From 686f3c29f80c00299eb7925a89204e8197428f7c Mon Sep 17 00:00:00 2001 From: rose Date: Wed, 11 Dec 2024 16:43:00 -0600 Subject: [PATCH 73/85] add comment --- moped-editor/src/components/DataGridPro/DataGridActions.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/moped-editor/src/components/DataGridPro/DataGridActions.js b/moped-editor/src/components/DataGridPro/DataGridActions.js index 8ceb6dca44..ba8065923a 100644 --- a/moped-editor/src/components/DataGridPro/DataGridActions.js +++ b/moped-editor/src/components/DataGridPro/DataGridActions.js @@ -37,7 +37,12 @@ const DataGridActions = ({ const apiRef = useGridApiContext(); - // check if all the required fields are filled out + /** + * To make our row re-render while still in edit mode we need the useGridSelector hook + * which establishes a reactive binding with the grid state and allows us to enable the save button + * if we have all the required fields. + * For reference https://mui.com/x/react-data-grid/state/#access-the-state + */ const hasRequiredFields = useGridSelector(apiRef, () => { const editState = apiRef.current.state.editRows; for (const field of requiredFields) { From 1ebf8ba8a91e684deea6a0371729901030027d49 Mon Sep 17 00:00:00 2001 From: rose Date: Thu, 12 Dec 2024 16:06:21 -0600 Subject: [PATCH 74/85] update deleteclick function call, use default prop, update int to num --- moped-editor/src/components/DataGridPro/DataGridActions.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/moped-editor/src/components/DataGridPro/DataGridActions.js b/moped-editor/src/components/DataGridPro/DataGridActions.js index ba8065923a..fa682daed1 100644 --- a/moped-editor/src/components/DataGridPro/DataGridActions.js +++ b/moped-editor/src/components/DataGridPro/DataGridActions.js @@ -14,7 +14,7 @@ import { import { defaultEditColumnIconStyle } from "src/utils/dataGridHelpers"; /** Component for Data Grid table action buttons - * @param {Integer} id - Data Grid row id (same as project id) + * @param {Number} id - Data Grid row id (same as project id) * @param {Array} requiredFields - fields that are required in order to save row * @param {Object} rowModesModel - row modes state from data grid * @param {Function} handleCancelClick - handles cancel button click @@ -26,7 +26,7 @@ import { defaultEditColumnIconStyle } from "src/utils/dataGridHelpers"; const DataGridActions = ({ id, - requiredFields, + requiredFields = [], rowModesModel, handleCancelClick, handleDeleteOpen, @@ -87,7 +87,7 @@ const DataGridActions = ({ } label="Delete" - onClick={() => handleDeleteOpen(id)} + onClick={handleDeleteOpen(id)} color="inherit" />, ]; From 2a000821c1d7b7a130dea6e19471f2f66ff9730e Mon Sep 17 00:00:00 2001 From: rose Date: Thu, 12 Dec 2024 16:08:23 -0600 Subject: [PATCH 75/85] create a required fields const, use a curried function --- .../views/projects/projectView/ProjectFiles.js | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/moped-editor/src/views/projects/projectView/ProjectFiles.js b/moped-editor/src/views/projects/projectView/ProjectFiles.js index 8ffff29c58..c880610978 100644 --- a/moped-editor/src/views/projects/projectView/ProjectFiles.js +++ b/moped-editor/src/views/projects/projectView/ProjectFiles.js @@ -64,6 +64,8 @@ const fileTypes = ["", "Funding", "Plans", "Estimates", "Other"]; // 'private/project/65/80_04072022191747_40d4c982e064d0f9_1800halfscofieldridgepwkydesignprint.pdf' const cleanUpFileKey = (str) => str.replace(/^(?:[^_]*_){3}/g, ""); +const requiredFields = ["file_name", "file_url", "file_type"]; + const useColumns = ({ classes, token, @@ -208,7 +210,7 @@ const useColumns = ({ renderCell: ({ id }) => ( { setDialogOpen(false); }; - const handleDeleteOpen = useCallback((id) => { - setIsDeleteConfirmationOpen(true); - setDeleteConfirmationId(id); - }, []); + const handleDeleteOpen = useCallback( + (id) => () => { + setIsDeleteConfirmationOpen(true); + setDeleteConfirmationId(id); + }, + [] + ); /** * Persists the file data into the database From dbd7f526f69c98d77965980cf05dda2d817599b8 Mon Sep 17 00:00:00 2001 From: rose Date: Thu, 12 Dec 2024 16:09:05 -0600 Subject: [PATCH 76/85] add requiredfields const and make delete open a curried function --- .../ProjectFunding/ProjectFundingTable.js | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/moped-editor/src/views/projects/projectView/ProjectFunding/ProjectFundingTable.js b/moped-editor/src/views/projects/projectView/ProjectFunding/ProjectFundingTable.js index f0b34ea4d3..13a8fa9cf5 100644 --- a/moped-editor/src/views/projects/projectView/ProjectFunding/ProjectFundingTable.js +++ b/moped-editor/src/views/projects/projectView/ProjectFunding/ProjectFundingTable.js @@ -223,7 +223,6 @@ const useColumns = ({ renderCell: ({ id }) => ( { refetch(); }; - const handleDeleteOpen = useCallback((id) => { - setIsDeleteConfirmationOpen(true); - setDeleteConfirmationId(id); - }, []); + const handleDeleteOpen = useCallback( + (id) => () => { + setIsDeleteConfirmationOpen(true); + setDeleteConfirmationId(id); + }, + [] + ); const fdusArray = useFdusArray(data?.moped_proj_funding); From 256e1a23c54ec7cdc9ce3b7686f95880eb6f7a7a Mon Sep 17 00:00:00 2001 From: rose Date: Thu, 12 Dec 2024 16:10:03 -0600 Subject: [PATCH 77/85] add required fields const and make delete open a curried function --- .../projects/projectView/ProjectMilestones.js | 15 ++++++++++----- .../ProjectSummary/SubprojectsTable.js | 15 ++++++++++----- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/moped-editor/src/views/projects/projectView/ProjectMilestones.js b/moped-editor/src/views/projects/projectView/ProjectMilestones.js index 74bd2d8766..fcab3315ba 100644 --- a/moped-editor/src/views/projects/projectView/ProjectMilestones.js +++ b/moped-editor/src/views/projects/projectView/ProjectMilestones.js @@ -54,6 +54,8 @@ const useMilestoneRelatedPhaseLookup = (data) => ); }, [data]); +const requiredFields = ["milestone_id"]; + const useColumns = ({ rowModesModel, handleEditClick, @@ -159,7 +161,7 @@ const useColumns = ({ renderCell: ({ id }) => ( { const relatedPhaseLookup = useMilestoneRelatedPhaseLookup(data); const phaseNameLookup = usePhaseNameLookup(data?.moped_phases || []); - const handleDeleteOpen = useCallback((id) => { - setIsDeleteConfirmationOpen(true); - setDeleteConfirmationId(id); - }, []); + const handleDeleteOpen = useCallback( + (id) => () => { + setIsDeleteConfirmationOpen(true); + setDeleteConfirmationId(id); + }, + [] + ); const handleRowModesModelChange = (newRowModesModel) => { setRowModesModel(newRowModesModel); diff --git a/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectsTable.js b/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectsTable.js index 816c69e6a1..0219ba1507 100644 --- a/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectsTable.js +++ b/moped-editor/src/views/projects/projectView/ProjectSummary/SubprojectsTable.js @@ -20,6 +20,8 @@ import { DELETE_PROJECT_SUBPROJECT, } from "../../../../queries/subprojects"; +const requiredFields = ["project_name_full"]; + /** Hook that provides memoized column settings */ const useColumns = ({ data, @@ -82,7 +84,7 @@ const useColumns = ({ renderCell: ({ id }) => ( { ); // open the delete confirmation modal - const handleDeleteOpen = useCallback((id) => { - setIsDeleteConfirmationOpen(true); - setDeleteConfirmationId(id); - }, []); + const handleDeleteOpen = useCallback( + (id) => () => { + setIsDeleteConfirmationOpen(true); + setDeleteConfirmationId(id); + }, + [] + ); // handles row delete const handleDeleteClick = (id) => () => { From 05af36004b411d555f864079e5cd48382527ecc2 Mon Sep 17 00:00:00 2001 From: rose Date: Thu, 12 Dec 2024 16:10:28 -0600 Subject: [PATCH 78/85] use helper text --- .../MilestoneAutocompleteComponent.js | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/moped-editor/src/views/projects/projectView/ProjectMilestones/MilestoneAutocompleteComponent.js b/moped-editor/src/views/projects/projectView/ProjectMilestones/MilestoneAutocompleteComponent.js index 0a7868e34e..fb2ada1ff7 100644 --- a/moped-editor/src/views/projects/projectView/ProjectMilestones/MilestoneAutocompleteComponent.js +++ b/moped-editor/src/views/projects/projectView/ProjectMilestones/MilestoneAutocompleteComponent.js @@ -1,10 +1,5 @@ import React from "react"; -import { - Autocomplete, - TextField, - FormHelperText, - FormControl, -} from "@mui/material"; +import { Autocomplete, TextField, FormControl } from "@mui/material"; import { useGridApiContext } from "@mui/x-data-grid-pro"; /** @@ -68,11 +63,11 @@ const MilestoneAutocompleteComponent = ({ variant="standard" {...params} inputRef={ref} - error={error} + error={error || !value} + helperText="Required" /> )} /> - Required ); }; From 5fd237296ea4395235f0f00c26652aeffba67505 Mon Sep 17 00:00:00 2001 From: tillyw Date: Fri, 13 Dec 2024 13:59:58 -0600 Subject: [PATCH 79/85] update migration --- .../1730324794396_add_moped_user_saved_views_table/up.sql | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/moped-database/migrations/1730324794396_add_moped_user_saved_views_table/up.sql b/moped-database/migrations/1730324794396_add_moped_user_saved_views_table/up.sql index 682d346f2c..420d4a7134 100644 --- a/moped-database/migrations/1730324794396_add_moped_user_saved_views_table/up.sql +++ b/moped-database/migrations/1730324794396_add_moped_user_saved_views_table/up.sql @@ -2,7 +2,7 @@ CREATE TABLE public.moped_user_saved_views ( id serial NOT NULL, description text NOT NULL, url text NOT NULL, - query_filters jsonb, + query_filters jsonb NOT NULL, created_by_user_id int4 NOT NULL, updated_by_user_id int4 NOT NULL, created_at timestamptz NOT NULL DEFAULT now(), @@ -22,7 +22,7 @@ COMMENT ON COLUMN moped_user_saved_views.created_by_user_id IS 'User ID of the c COMMENT ON COLUMN moped_user_saved_views.updated_by_user_id IS 'User ID of the last updater of the view'; COMMENT ON COLUMN moped_user_saved_views.created_at IS 'Timestamp of when the view was created'; COMMENT ON COLUMN moped_user_saved_views.updated_at IS 'Timestamp of the last update of the view'; -COMMENT ON COLUMN moped_user_saved_views.query_filters IS 'Boolean indicating whether the view has been soft deleted and thereby not rendered in the UI'; +COMMENT ON COLUMN moped_user_saved_views.is_deleted IS 'Boolean indicating whether the view has been soft deleted and thereby not rendered in the UI'; -- Adding comments for moped_user_saved_views constraints COMMENT ON CONSTRAINT fk_moped_user_saved_views_created_by ON moped_user_saved_views IS 'Foreign key constraint linking created_by_user_id to moped_users table.'; From 071a21c5aefa1c85d2b3c593f29dee6dfa7ce2af Mon Sep 17 00:00:00 2001 From: Moped View Bot Date: Fri, 13 Dec 2024 20:02:09 +0000 Subject: [PATCH 80/85] =?UTF-8?q?=F0=9F=A4=96=20Export=20view=20for=201968?= =?UTF-8?q?6=5Fadd=5Fmoped=5Fuser=5Fsaved=5Fviews=5Fto=5Fdatabase?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- moped-database/views/project_list_view.sql | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/moped-database/views/project_list_view.sql b/moped-database/views/project_list_view.sql index 86e3824226..31e4e28466 100644 --- a/moped-database/views/project_list_view.sql +++ b/moped-database/views/project_list_view.sql @@ -15,8 +15,14 @@ CREATE OR REPLACE VIEW project_list_view AS WITH project_person_list_lookup AS ( funding_sources_lookup AS ( SELECT mpf.project_id, - string_agg(DISTINCT mfs.funding_source_name, ', '::text ORDER BY mfs.funding_source_name) AS funding_source_name, - string_agg(DISTINCT mfp.funding_program_name, ', '::text ORDER BY mfp.funding_program_name) AS funding_program_names, + string_agg( + DISTINCT mfs.funding_source_name, ', '::text + ORDER BY mfs.funding_source_name + ) AS funding_source_name, + string_agg( + DISTINCT mfp.funding_program_name, ', '::text + ORDER BY mfp.funding_program_name + ) AS funding_program_names, string_agg( DISTINCT CASE @@ -24,7 +30,8 @@ funding_sources_lookup AS ( WHEN mfs.funding_source_name IS NOT null THEN mfs.funding_source_name WHEN mfp.funding_program_name IS NOT null THEN mfp.funding_program_name ELSE null::text - END, ', '::text ORDER BY ( + END, ', '::text + ORDER BY ( CASE WHEN mfs.funding_source_name IS NOT null AND mfp.funding_program_name IS NOT null THEN concat(mfs.funding_source_name, ' - ', mfp.funding_program_name) WHEN mfs.funding_source_name IS NOT null THEN mfs.funding_source_name @@ -170,7 +177,10 @@ min_estimated_phase_dates AS ( project_component_work_types AS ( SELECT mpc.project_id, - string_agg(DISTINCT mwt.name, ', '::text ORDER BY mwt.name) AS component_work_type_names + string_agg( + DISTINCT mwt.name, ', '::text + ORDER BY mwt.name + ) AS component_work_type_names FROM moped_proj_components mpc LEFT JOIN moped_proj_component_work_types mpcwt ON mpcwt.project_component_id = mpc.project_component_id LEFT JOIN moped_work_types mwt ON mwt.id = mpcwt.work_type_id From 99b48e5801e2fda9f88638c584c492ac11f74773 Mon Sep 17 00:00:00 2001 From: Mike Date: Fri, 13 Dec 2024 11:53:55 -0600 Subject: [PATCH 81/85] Add migrations to add/remove ss4a project tags --- .../migrations/1734112355149_add_ss4a_proj_tags/down.sql | 5 +++++ .../migrations/1734112355149_add_ss4a_proj_tags/up.sql | 3 +++ 2 files changed, 8 insertions(+) create mode 100644 moped-database/migrations/1734112355149_add_ss4a_proj_tags/down.sql create mode 100644 moped-database/migrations/1734112355149_add_ss4a_proj_tags/up.sql diff --git a/moped-database/migrations/1734112355149_add_ss4a_proj_tags/down.sql b/moped-database/migrations/1734112355149_add_ss4a_proj_tags/down.sql new file mode 100644 index 0000000000..1efb213b08 --- /dev/null +++ b/moped-database/migrations/1734112355149_add_ss4a_proj_tags/down.sql @@ -0,0 +1,5 @@ +-- Could not auto-generate a down migration. +-- Please write an appropriate down migration for the SQL below: +-- INSERT INTO moped_tags (name, type, slug, is_deleted) VALUES +-- ('SS4A Vision Zero 2022', 'Work Plan', 'ss4a_vision_zero_2022', FALSE), +-- ('SS4A Vision Zero 2024', 'Work Plan', 'ss4a_vision_zero_2024', FALSE); diff --git a/moped-database/migrations/1734112355149_add_ss4a_proj_tags/up.sql b/moped-database/migrations/1734112355149_add_ss4a_proj_tags/up.sql new file mode 100644 index 0000000000..6a2a986128 --- /dev/null +++ b/moped-database/migrations/1734112355149_add_ss4a_proj_tags/up.sql @@ -0,0 +1,3 @@ +INSERT INTO moped_tags (name, type, slug, is_deleted) VALUES +('SS4A Vision Zero 2022', 'Work Plan', 'ss4a_vision_zero_2022', FALSE), +('SS4A Vision Zero 2024', 'Work Plan', 'ss4a_vision_zero_2024', FALSE); From a713f0e7cbd5083c18f28231f6f811bd10922a86 Mon Sep 17 00:00:00 2001 From: Mike Date: Fri, 13 Dec 2024 11:56:54 -0600 Subject: [PATCH 82/85] Fill out down migration --- .../migrations/1734112355149_add_ss4a_proj_tags/down.sql | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/moped-database/migrations/1734112355149_add_ss4a_proj_tags/down.sql b/moped-database/migrations/1734112355149_add_ss4a_proj_tags/down.sql index 1efb213b08..9ab023f783 100644 --- a/moped-database/migrations/1734112355149_add_ss4a_proj_tags/down.sql +++ b/moped-database/migrations/1734112355149_add_ss4a_proj_tags/down.sql @@ -1,5 +1 @@ --- Could not auto-generate a down migration. --- Please write an appropriate down migration for the SQL below: --- INSERT INTO moped_tags (name, type, slug, is_deleted) VALUES --- ('SS4A Vision Zero 2022', 'Work Plan', 'ss4a_vision_zero_2022', FALSE), --- ('SS4A Vision Zero 2024', 'Work Plan', 'ss4a_vision_zero_2024', FALSE); +DELETE FROM public.moped_tags WHERE slug = 'ss4a_vision_zero_2022' OR slug = 'ss4a_vision_zero_2024'; From 87c228cf745281b6b1f2067cad011bda6001d895 Mon Sep 17 00:00:00 2001 From: Mike Date: Fri, 13 Dec 2024 12:00:41 -0600 Subject: [PATCH 83/85] Patch version bump --- moped-editor/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/moped-editor/package.json b/moped-editor/package.json index e94f10e23f..5cb65d0f6f 100644 --- a/moped-editor/package.json +++ b/moped-editor/package.json @@ -2,7 +2,7 @@ "name": "atd-moped-editor", "author": "ATD Data & Technology Services", "license": "CC0-1.0", - "version": "2.25.0", + "version": "2.25.1", "homepage": "/moped", "private": false, "repository": { From 31075162d216c9ec4e4de4da0c0c4f6e0f9037ed Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 18 Dec 2024 16:14:04 +0000 Subject: [PATCH 84/85] Bump nanoid from 3.3.7 to 3.3.8 in /moped-editor Bumps [nanoid](https://github.com/ai/nanoid) from 3.3.7 to 3.3.8. - [Release notes](https://github.com/ai/nanoid/releases) - [Changelog](https://github.com/ai/nanoid/blob/main/CHANGELOG.md) - [Commits](https://github.com/ai/nanoid/compare/3.3.7...3.3.8) --- updated-dependencies: - dependency-name: nanoid dependency-type: indirect ... Signed-off-by: dependabot[bot] --- moped-editor/package-lock.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/moped-editor/package-lock.json b/moped-editor/package-lock.json index 82f1109718..5f182c0fb3 100644 --- a/moped-editor/package-lock.json +++ b/moped-editor/package-lock.json @@ -1,12 +1,12 @@ { "name": "atd-moped-editor", - "version": "2.24.0", + "version": "2.25.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "atd-moped-editor", - "version": "2.24.0", + "version": "2.25.1", "hasInstallScript": true, "license": "CC0-1.0", "dependencies": { @@ -28036,9 +28036,9 @@ } }, "node_modules/nanoid": { - "version": "3.3.7", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", - "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", + "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==", "funding": [ { "type": "github", From 12ae3a98192708a7771109523b26327504ca2953 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 18 Dec 2024 16:51:36 +0000 Subject: [PATCH 85/85] Bump path-to-regexp and express in /moped-editor Bumps [path-to-regexp](https://github.com/pillarjs/path-to-regexp) and [express](https://github.com/expressjs/express). These dependencies needed to be updated together. Updates `path-to-regexp` from 0.1.10 to 0.1.12 - [Release notes](https://github.com/pillarjs/path-to-regexp/releases) - [Changelog](https://github.com/pillarjs/path-to-regexp/blob/master/History.md) - [Commits](https://github.com/pillarjs/path-to-regexp/compare/v0.1.10...v0.1.12) Updates `express` from 4.21.1 to 4.21.2 - [Release notes](https://github.com/expressjs/express/releases) - [Changelog](https://github.com/expressjs/express/blob/4.21.2/History.md) - [Commits](https://github.com/expressjs/express/compare/4.21.1...4.21.2) --- updated-dependencies: - dependency-name: path-to-regexp dependency-type: indirect - dependency-name: express dependency-type: indirect ... Signed-off-by: dependabot[bot] --- moped-editor/package-lock.json | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/moped-editor/package-lock.json b/moped-editor/package-lock.json index 5f182c0fb3..4850980770 100644 --- a/moped-editor/package-lock.json +++ b/moped-editor/package-lock.json @@ -20108,9 +20108,9 @@ } }, "node_modules/express": { - "version": "4.21.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.21.1.tgz", - "integrity": "sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==", + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", + "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", @@ -20131,7 +20131,7 @@ "methods": "~1.1.2", "on-finished": "2.4.1", "parseurl": "~1.3.3", - "path-to-regexp": "0.1.10", + "path-to-regexp": "0.1.12", "proxy-addr": "~2.0.7", "qs": "6.13.0", "range-parser": "~1.2.1", @@ -20146,6 +20146,10 @@ }, "engines": { "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/express/node_modules/cookie": { @@ -28972,9 +28976,9 @@ } }, "node_modules/path-to-regexp": { - "version": "0.1.10", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz", - "integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==" + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", + "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==" }, "node_modules/path-type": { "version": "4.0.0",