Skip to content

Commit

Permalink
Make widget title editable (#1961)
Browse files Browse the repository at this point in the history
* Make widget title updatable

* Add widget title to all components

* Show the widget.title in the engagement view

* remove unsued imports

* fix lint issues

* fix flake8 issue

* Fix failing tests
  • Loading branch information
jadmsaadaot authored Aug 4, 2023
1 parent 69a5cf3 commit 97d7622
Show file tree
Hide file tree
Showing 41 changed files with 379 additions and 136 deletions.
14 changes: 14 additions & 0 deletions met-api/src/met_api/models/widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"""
from __future__ import annotations
from datetime import datetime
from typing import Optional

from sqlalchemy.sql.schema import ForeignKey

Expand Down Expand Up @@ -65,6 +66,7 @@ def __create_new_widget_entity(widget):
updated_date=datetime.utcnow(),
created_by=widget.get('created_by', None),
updated_by=widget.get('updated_by', None),
title=widget.get('title', None),
)

@classmethod
Expand All @@ -87,3 +89,15 @@ def update_widgets(cls, update_mappings: list) -> None:
"""Update widgets.."""
db.session.bulk_update_mappings(Widget, update_mappings)
db.session.commit()

@classmethod
def update_widget(cls, engagement_id, widget_id, widget_data: dict) -> Optional[Widget]:
"""Update widget."""
query = Widget.query.filter_by(id=widget_id, engagement_id=engagement_id)
widget: Widget = query.first()
if not widget:
return None
widget_data['updated_date'] = datetime.utcnow()
query.update(widget_data)
db.session.commit()
return widget
23 changes: 21 additions & 2 deletions met-api/src/met_api/resources/widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,8 @@ def patch(engagement_id):
return {'message': err.error}, err.status_code


@cors_preflight('DELETE')
@API.route('/engagement/<engagement_id>/widget/<widget_id>')
@cors_preflight('DELETE, PATCH')
@API.route('/<widget_id>/engagements/<engagement_id>')
class EngagementWidget(Resource):
"""Resource for managing widgets with engagements."""

Expand All @@ -107,6 +107,25 @@ def delete(engagement_id, widget_id):
except ValueError as err:
return str(err), HTTPStatus.INTERNAL_SERVER_ERROR

@staticmethod
@cross_origin(origins=allowedorigins())
@_jwt.requires_auth
def patch(engagement_id, widget_id):
"""Update widget."""
try:
user_id = TokenInfo.get_id()
widget_data = request.get_json()
valid_format, errors = schema_utils.validate(widget_data, 'widget_update')
if not valid_format:
return {'message': schema_utils.serialize(errors)}, HTTPStatus.BAD_REQUEST

updated_widget = WidgetService().update_widget(engagement_id, widget_id, widget_data, user_id)
return updated_widget, HTTPStatus.OK
except (KeyError, ValueError) as err:
return str(err), HTTPStatus.INTERNAL_SERVER_ERROR
except ValidationError as err:
return str(err.messages), HTTPStatus.INTERNAL_SERVER_ERROR


@cors_preflight('POST,OPTIONS')
@API.route('/<widget_id>/items')
Expand Down
23 changes: 23 additions & 0 deletions met-api/src/met_api/schemas/schemas/widget_update.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"$schema": "http://json-schema.org/draft-07/schema",
"$id": "https://met.gov.bc.ca/.well_known/schemas/widget_update",
"type": "object",
"title": "The root schema",
"description": "The root schema comprises the entire JSON document.",
"default": {},
"examples": [
{
"title": "Who is Listening"
}
],
"required": ["title"],
"properties": {
"title": {
"$id": "#/properties/title",
"type": "string",
"title": "Widget title",
"description": "The title of the widget.",
"examples": ["Who is Listening"]
}
}
}
1 change: 1 addition & 0 deletions met-api/src/met_api/schemas/widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ class Meta: # pylint: disable=too-few-public-methods
unknown = EXCLUDE

id = fields.Int(data_key='id')
title = fields.Str(data_key='title')
widget_type_id = fields.Int(data_key='widget_type_id', required=True)
engagement_id = fields.Int(data_key='engagement_id', required=True)
created_by = fields.Str(data_key='created_by')
Expand Down
18 changes: 18 additions & 0 deletions met-api/src/met_api/services/widget_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,16 @@ def sort_widget(engagement_id, widgets: list, user_id=None):

WidgetModel.update_widgets(widget_sort_mappings)

@staticmethod
def update_widget(engagement_id, widget_id: list, widget_data: dict, user_id=None):
"""Sort widgets."""
WidgetService._verify_widget(widget_id)

widget_data['updated_by'] = user_id

updated_widget = WidgetModel.update_widget(engagement_id, widget_id, widget_data)
return WidgetSchema().dump(updated_widget)

@staticmethod
def _validate_widget_ids(engagement_id, widgets):
"""Validate if widget ids belong to the engagement."""
Expand All @@ -76,6 +86,14 @@ def _validate_widget_ids(engagement_id, widgets):
error='Invalid widgets.',
status_code=HTTPStatus.BAD_REQUEST)

@staticmethod
def _verify_widget(widget_id):
"""Verify if widget exists."""
widget = WidgetModel.get_widget_by_id(widget_id)
if not widget:
raise KeyError('Widget ' + widget_id + ' does not exist')
return widget

@staticmethod
def create_widget_items_bulk(widget_items: list, user_id):
"""Create widget items in bulk."""
Expand Down
19 changes: 16 additions & 3 deletions met-web/src/apiManager/apiSlices/widgets/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,14 @@ export const widgetsApi = createApi({
}),
invalidatesTags: ['Widgets'],
}),
updateWidget: builder.mutation<Widget, { id: number; engagementId: number; data: Partial<Widget> }>({
query: ({ engagementId, id, data }) => ({
url: `widgets/${id}/engagements/${engagementId}`,
method: 'PATCH',
body: data,
}),
invalidatesTags: ['Widgets'],
}),
sortWidgets: builder.mutation<Widget, { engagementId: number; widgets: Widget[] }>({
query: ({ engagementId, widgets }) => ({
url: `widgets/engagement/${engagementId}/sort_index`,
Expand All @@ -35,7 +43,7 @@ export const widgetsApi = createApi({
}),
deleteWidget: builder.mutation<Widget, { engagementId: number; widgetId: number }>({
query: ({ engagementId, widgetId }) => ({
url: `widgets/engagement/${engagementId}/widget/${widgetId}`,
url: `widgets/${widgetId}/engagements/${engagementId}`,
method: 'DELETE',
}),
invalidatesTags: (_result, _error, arg) => [{ type: 'Widgets', id: arg.widgetId }],
Expand All @@ -47,5 +55,10 @@ export const widgetsApi = createApi({

// Export hooks for usage in functional components, which are
// auto-generated based on the defined endpoints
export const { useLazyGetWidgetsQuery, useCreateWidgetMutation, useSortWidgetsMutation, useDeleteWidgetMutation } =
widgetsApi;
export const {
useLazyGetWidgetsQuery,
useCreateWidgetMutation,
useSortWidgetsMutation,
useDeleteWidgetMutation,
useUpdateWidgetMutation,
} = widgetsApi;
2 changes: 1 addition & 1 deletion met-web/src/apiManager/endpoints/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ const Endpoints = {
Widgets: {
GET_LIST: `${AppConfig.apiUrl}/widgets/engagement/engagement_id`,
CREATE: `${AppConfig.apiUrl}/widgets/engagement/engagement_id`,
DELETE: `${AppConfig.apiUrl}/widgets/engagement/engagement_id/widget/widget_id`,
DELETE: `${AppConfig.apiUrl}/widgets/widget_id/engagements/engagement_id`,
SORT: `${AppConfig.apiUrl}/widgets/engagement/engagement_id/sort_index`,
},
Widget_items: {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,36 +1,41 @@
import React, { useContext } from 'react';
import { Divider, Grid } from '@mui/material';
import { MetHeader3, PrimaryButton } from 'components/common';
import { PrimaryButton } from 'components/common';
import { WidgetDrawerContext } from '../WidgetDrawerContext';
import CreateFolderForm from './CreateFolderForm';
import DocumentsBlock from './DocumentsBlock';
import { WidgetTitle } from '../WidgetTitle';
import { DocumentsContext } from './DocumentsContext';

const DocumentForm = () => {
const { handleWidgetDrawerOpen } = useContext(WidgetDrawerContext);
const { widget } = useContext(DocumentsContext);

if (!widget) {
return null;
}

return (
<>
<Grid item xs={12} container alignItems="flex-start" justifyContent={'flex-start'} spacing={3}>
<Grid item xs={12}>
<MetHeader3>Documents</MetHeader3>
<Divider sx={{ marginTop: '1em' }} />
</Grid>
<Grid item xs={12} container alignItems="flex-start" justifyContent={'flex-start'} spacing={3}>
<Grid item xs={12}>
<WidgetTitle widget={widget} />
<Divider sx={{ marginTop: '1em' }} />
</Grid>

<Grid item xs={12}>
<CreateFolderForm />
</Grid>
<Grid item xs={12}>
<CreateFolderForm />
</Grid>

<Grid item xs={12}>
<DocumentsBlock />
</Grid>
<Grid item xs={12}>
<DocumentsBlock />
</Grid>

<Grid item xs={12} container direction="row" spacing={1} justifyContent={'flex-start'} marginTop="8em">
<Grid item>
<PrimaryButton onClick={() => handleWidgetDrawerOpen(false)}>{`Close`}</PrimaryButton>
</Grid>
<Grid item xs={12} container direction="row" spacing={1} justifyContent={'flex-start'} marginTop="8em">
<Grid item>
<PrimaryButton onClick={() => handleWidgetDrawerOpen(false)}>{`Close`}</PrimaryButton>
</Grid>
</Grid>
</>
</Grid>
);
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { openNotification } from 'services/notificationService/notificationSlice
import { optionCardStyle } from '../constants';
import { useCreateWidgetMutation } from 'apiManager/apiSlices/widgets';

const Title = 'Documents';
const DocumentOptionCard = () => {
const { widgets, loadWidgets, handleWidgetDrawerTabValueChange } = useContext(WidgetDrawerContext);
const { savedEngagement } = useContext(ActionContext);
Expand All @@ -31,6 +32,7 @@ const DocumentOptionCard = () => {
await createWidget({
widget_type_id: WidgetType.Document,
engagement_id: savedEngagement.id,
title: Title,
});
await loadWidgets();
dispatch(
Expand Down Expand Up @@ -82,7 +84,7 @@ const DocumentOptionCard = () => {
xs={8}
>
<Grid item xs={12}>
<MetLabel>Documents</MetLabel>
<MetLabel>{Title}</MetLabel>
</Grid>
<Grid item xs={12}>
<MetDescription>Add documents and folders</MetDescription>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { optionCardStyle } from '../constants';
import { WidgetTabValues } from '../type';
import { useCreateWidgetMutation } from 'apiManager/apiSlices/widgets';

const Title = 'Events';
const EventsOptionCard = () => {
const { widgets, loadWidgets, handleWidgetDrawerOpen, handleWidgetDrawerTabValueChange } =
useContext(WidgetDrawerContext);
Expand All @@ -32,6 +33,7 @@ const EventsOptionCard = () => {
await createWidget({
widget_type_id: WidgetType.Events,
engagement_id: savedEngagement.id,
title: Title,
});
await loadWidgets();
dispatch(
Expand Down Expand Up @@ -83,7 +85,7 @@ const EventsOptionCard = () => {
xs={8}
>
<Grid item xs={12}>
<MetLabel>Events</MetLabel>
<MetLabel>{Title}</MetLabel>
</Grid>
<Grid item xs={12}>
<MetDescription>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,23 @@
import React, { useContext } from 'react';
import { Grid, Divider } from '@mui/material';
import { PrimaryButton, MetHeader3, WidgetButton } from 'components/common';
import { PrimaryButton, WidgetButton } from 'components/common';
import { WidgetDrawerContext } from '../WidgetDrawerContext';
import { EventsContext } from './EventsContext';
import EventsInfoBlock from './EventsInfoBlock';
import { WidgetTitle } from '../WidgetTitle';

const Form = () => {
const { handleWidgetDrawerOpen } = useContext(WidgetDrawerContext);
const { setInPersonFormTabOpen, setVirtualSessionFormTabOpen } = useContext(EventsContext);
const { setInPersonFormTabOpen, setVirtualSessionFormTabOpen, widget } = useContext(EventsContext);

if (!widget) {
return null;
}

return (
<Grid item xs={12} container alignItems="flex-start" justifyContent={'flex-start'} spacing={3}>
<Grid item xs={12}>
<MetHeader3 bold>Events</MetHeader3>
<WidgetTitle widget={widget} />
<Divider sx={{ marginTop: '1em' }} />
</Grid>
<Grid item xs={12} container direction="row" spacing={1} justifyContent={'flex-start'}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import React, { useContext, useState, useEffect } from 'react';
import Divider from '@mui/material/Divider';
import { Grid, Typography, Stack, IconButton } from '@mui/material';
import {
MetHeader3,
MetLabel,
PrimaryButton,
SecondaryButton,
Expand All @@ -26,6 +25,7 @@ import LinkIcon from '@mui/icons-material/Link';
import { When } from 'react-if';
import HighlightOffIcon from '@mui/icons-material/HighlightOff';
import * as turf from '@turf/turf';
import { WidgetTitle } from '../WidgetTitle';

const schema = yup
.object({
Expand Down Expand Up @@ -163,10 +163,14 @@ const Form = () => {
);
}

if (!widget) {
return null;
}

return (
<Grid item xs={12} container alignItems="flex-start" justifyContent={'flex-start'} spacing={3}>
<Grid item xs={12}>
<MetHeader3 bold>Map</MetHeader3>
<WidgetTitle widget={widget} />
<Divider sx={{ marginTop: '1em' }} />
</Grid>
<Grid item xs={12}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { WidgetTabValues } from '../type';
import LocationOnIcon from '@mui/icons-material/LocationOn';
import { useCreateWidgetMutation } from 'apiManager/apiSlices/widgets';

const Title = 'Map';
const MapOptionCard = () => {
const { widgets, loadWidgets, handleWidgetDrawerOpen, handleWidgetDrawerTabValueChange } =
useContext(WidgetDrawerContext);
Expand All @@ -32,6 +33,7 @@ const MapOptionCard = () => {
await createWidget({
widget_type_id: WidgetType.Map,
engagement_id: savedEngagement.id,
title: Title,
});
await loadWidgets();
dispatch(
Expand Down Expand Up @@ -83,7 +85,7 @@ const MapOptionCard = () => {
xs={8}
>
<Grid item xs={12}>
<MetLabel>Map</MetLabel>
<MetLabel>{Title}</MetLabel>
</Grid>
<Grid item xs={12}>
<MetDescription>Add a map with the project location</MetDescription>
Expand Down
Loading

0 comments on commit 97d7622

Please sign in to comment.