Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make widget title editable #1961

Merged
merged 7 commits into from
Aug 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading