From 98a043e4c4383579a897997f84951a91edffffeb Mon Sep 17 00:00:00 2001 From: Arturo Reyes Lopez Date: Wed, 4 Sep 2024 10:03:30 -0600 Subject: [PATCH] Code review changes. --- .../Controllers/TonePoolController.cs | 23 ++- .../edit/constants/defaultContentTonePool.ts | 8 + .../edit/content/ContentEditForm.tsx | 104 +++++----- .../edit/content/stories/ContentForm.tsx | 179 +++++++----------- .../edit/content/stories/ToneSelector.tsx | 61 ++++++ .../content/stories/styled/ToneSelector.tsx | 62 ++++++ .../edit/content/stories/styled/index.ts | 1 + .../edit/validation/SentimentFormSchema.ts | 9 - .../src/store/hooks/lookup/useLookup.ts | 18 +- .../profile/interfaces/IProfileState.ts | 2 +- .../src/store/slices/profile/profileSlice.ts | 10 +- .../store/slices/profile/useProfileStore.ts | 11 +- libs/net/dal/Extensions/ContentExtensions.cs | 24 +-- libs/net/dal/Services/TonePoolService.cs | 2 +- libs/net/entities/Models/ContentVersion.cs | 7 +- .../formik/sentiment/FormikSentiment.tsx | 68 ++----- .../sentiment/styled/FormikSentiment.tsx | 6 - .../api/interfaces/IContentTonePoolModel.ts | 2 +- .../api/interfaces/IContentVersionModel.ts | 1 - .../src/hooks/api/interfaces/ITonePool.ts | 5 + .../core/src/hooks/api/interfaces/index.ts | 1 + .../subscriber/useApiSubscriberTonePools.ts | 1 - libs/npm/core/tsconfig.json | 20 +- libs/npm/core/webpack.config.js | 9 - 24 files changed, 332 insertions(+), 302 deletions(-) create mode 100644 app/subscriber/src/features/my-reports/edit/constants/defaultContentTonePool.ts create mode 100644 app/subscriber/src/features/my-reports/edit/content/stories/ToneSelector.tsx create mode 100644 app/subscriber/src/features/my-reports/edit/content/stories/styled/ToneSelector.tsx delete mode 100644 app/subscriber/src/features/my-reports/edit/validation/SentimentFormSchema.ts create mode 100644 libs/npm/core/src/hooks/api/interfaces/ITonePool.ts diff --git a/api/net/Areas/Subscriber/Controllers/TonePoolController.cs b/api/net/Areas/Subscriber/Controllers/TonePoolController.cs index 727ef7bee9..0c02cd33a3 100644 --- a/api/net/Areas/Subscriber/Controllers/TonePoolController.cs +++ b/api/net/Areas/Subscriber/Controllers/TonePoolController.cs @@ -48,7 +48,7 @@ public TonePoolController(ITonePoolService service) #region Endpoints /// - /// Return an array of TonePool. + /// Find all TonePools. /// /// [HttpGet, HttpHead] @@ -56,11 +56,24 @@ public TonePoolController(ITonePoolService service) [ProducesResponseType(typeof(IEnumerable), (int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.NotModified)] [SwaggerOperation(Tags = new[] { "TonePool" })] - [ETagCacheTableFilter("tone_pools")] - [ResponseCache(Duration = 5 * 60)] + public IActionResult FindAll() + { + return new JsonResult(_service.FindAll()); + } + + /// + /// Find a TonePool by 'id'. + /// + /// + /// + [HttpGet("{id}")] + [Produces(MediaTypeNames.Application.Json)] + [ProducesResponseType(typeof(TonePoolModel), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(ErrorResponseModel), (int)HttpStatusCode.BadRequest)] + [SwaggerOperation(Tags = new[] { "TonePool" })] public IActionResult FindById(int id) { - var result = _service.FindById(id) ?? throw new NoContentException(); + var result = _service.FindById(id) ?? throw new NoContentException("TonePool does not exist"); return new JsonResult(new TonePoolModel(result)); } @@ -95,8 +108,6 @@ public IActionResult FindByUserId(int userId) [ProducesResponseType(typeof(TonePoolModel), (int)HttpStatusCode.Created)] [ProducesResponseType(typeof(ErrorResponseModel), (int)HttpStatusCode.BadRequest)] [SwaggerOperation(Tags = new[] { "TonePool" })] - [ETagCacheTableFilter("tone_pools")] - [ResponseCache(Duration = 5 * 60)] public IActionResult Add(TonePoolModel model) { var tonePoolEntity = new TonePool(model.Name, model.OwnerId) diff --git a/app/subscriber/src/features/my-reports/edit/constants/defaultContentTonePool.ts b/app/subscriber/src/features/my-reports/edit/constants/defaultContentTonePool.ts new file mode 100644 index 0000000000..8bef6e3dad --- /dev/null +++ b/app/subscriber/src/features/my-reports/edit/constants/defaultContentTonePool.ts @@ -0,0 +1,8 @@ +import { IContentTonePoolModel } from 'tno-core'; + +import { defaultTonePool } from './defaultTonePool'; + +export const defaultContentTonePool: IContentTonePoolModel = { + ...defaultTonePool, + value: undefined, +}; diff --git a/app/subscriber/src/features/my-reports/edit/content/ContentEditForm.tsx b/app/subscriber/src/features/my-reports/edit/content/ContentEditForm.tsx index 162a8602d5..44b0c150ab 100644 --- a/app/subscriber/src/features/my-reports/edit/content/ContentEditForm.tsx +++ b/app/subscriber/src/features/my-reports/edit/content/ContentEditForm.tsx @@ -6,12 +6,12 @@ import { IContentValidationErrors } from 'features/my-reports/interfaces/IConten import { toForm } from 'features/my-reports/utils'; import { formatDate } from 'features/utils'; import React from 'react'; +import { FaToggleOff, FaToggleOn } from 'react-icons/fa'; import { useApp, useContent, useReports } from 'store/hooks'; import { useTonePool } from 'store/hooks/subscriber/useTonePool'; -import { useProfileStore } from 'store/slices'; -import { Col, ContentTypeName, IContentModel, IContentTonePoolModel } from 'tno-core'; +import { Col, ContentTypeName, IContentModel, IContentTonePoolModel, ToggleButton } from 'tno-core'; -import { defaultTonePool } from '../constants/defaultTonePool'; +import { defaultContentTonePool } from '../constants/defaultContentTonePool'; import { useReportEditContext } from '../ReportEditContext'; import { ContentActions, ContentForm, UserContentForm } from './stories'; import * as styled from './styled'; @@ -30,10 +30,9 @@ export interface IContentEditFormProps { export const ContentEditForm = React.forwardRef( ({ disabled }, ref) => { const [{ userInfo }] = useApp(); - const [{ myTonePool, init }, { storeMyTonePool }] = useProfileStore(); const [, { updateReport }] = useReports(); const [, { addContent, updateContentSilent, getContent }] = useContent(); - const [, { addMyTonePool, getMyTonePool }] = useTonePool(); + const [, { addMyTonePool }] = useTonePool(); const { values, onNavigate, isSubmitting, setSubmitting, setValues, activeRow, setActiveRow } = useReportEditContext(); @@ -45,44 +44,11 @@ export const ContentEditForm = React.forwardRef { - const getTonePool = async () => { - try { - if (!init.myTonePool && userId !== 0) { - const response = await getMyTonePool(userId); - if (response?.id) { - storeMyTonePool(response); - } else { - // If no valid tone pool exists, proceed to create one - await createTonePool(userId); - } - } - } catch (error) { - console.error('Error loading tone pool:', error); - } finally { - } - }; - - const createTonePool = async (userId: number) => { - try { - await addMyTonePool({ - ...defaultTonePool, - name: `${userId}`, - ownerId: userId, - }); - const newTonePool = await getMyTonePool(userId); - storeMyTonePool(newTonePool); - } catch (error) { - console.error('Error creating tone pool:', error); - } finally { - } - }; - - if (myTonePool.id === 0) { - getTonePool(); - } - }, [getMyTonePool, addMyTonePool, myTonePool.id, userId, init.myTonePool, storeMyTonePool]); + const toggleSentiment = React.useCallback(() => { + setIsUserSentiment((prev) => !prev); + }, []); React.useEffect(() => { const updatedUserFormTonePool = @@ -93,6 +59,7 @@ export const ContentEditForm = React.forwardRef { setForm(activeRow); + setIsUserSentiment(false); }, [activeRow]); const validate = (values: IContentModel) => { @@ -133,6 +100,23 @@ export const ContentEditForm = React.forwardRef { + try { + const newTonePool = await addMyTonePool({ + ...defaultContentTonePool, + name: `${userId}`, + ownerId: userId, + }); + return newTonePool; + } catch (error) { + console.error('Error creating tone pool:', error); + } finally { + } + }, + [addMyTonePool], + ); + const handleAddUpdateContent = React.useCallback( async (values: IReportForm, row: IReportInstanceContentForm) => { try { @@ -143,6 +127,25 @@ export const ContentEditForm = React.forwardRef tp.ownerId === userId) ?? defaultContentTonePool; + if (userTonePool.id === 0) { + const newTonePool = await createTonePool(userId); + + if (newTonePool) { + userTonePool = { + ...userTonePool, + id: newTonePool.id, + }; + } + if (content?.tonePools) { + content.tonePools = [ + ...content.tonePools.filter((tp) => tp.ownerId !== userId), + userTonePool, + ]; + } + } + if (err.hasErrors) return null; try { contentResult = !content.id ? await addContent(content) @@ -225,6 +228,7 @@ export const ContentEditForm = React.forwardRef + } + off={} + onClick={toggleSentiment} + width="25px" + height="25px" + color="#6750a4" + label="User Tone" + value={isUserSentiment} + /> 0 ? form.content.tonePools[0].value : undefined @@ -347,6 +362,7 @@ export const ContentEditForm = React.forwardRef { setForm({ ...form, content }); }} + isUserSentiment={isUserSentiment} loading={isSubmitting} disabled={disabled} /> diff --git a/app/subscriber/src/features/my-reports/edit/content/stories/ContentForm.tsx b/app/subscriber/src/features/my-reports/edit/content/stories/ContentForm.tsx index 79233933bf..5f9f322d81 100644 --- a/app/subscriber/src/features/my-reports/edit/content/stories/ContentForm.tsx +++ b/app/subscriber/src/features/my-reports/edit/content/stories/ContentForm.tsx @@ -1,11 +1,10 @@ -import { Form, Formik } from 'formik'; import React from 'react'; -import { useApp, useLookup } from 'store/hooks'; +import { useApp } from 'store/hooks'; +import { useTonePool } from 'store/hooks/subscriber/useTonePool'; import { useProfileStore } from 'store/slices'; import { Col, ContentTypeName, - FormikSentiment, IContentModel, IContentTonePoolModel, Loading, @@ -15,7 +14,8 @@ import { Wysiwyg, } from 'tno-core'; -import { sentimentFormSchema } from '../../validation/SentimentFormSchema'; +import { defaultContentTonePool } from '../../constants/defaultContentTonePool'; +import { ToneSelector } from './ToneSelector'; export interface IContentFormProps extends Omit, 'content'> { /** The content being edited */ @@ -28,6 +28,8 @@ export interface IContentFormProps extends Omit void; + /** User has tone pool defined*/ + isUserSentiment: boolean; reportContent: { label: string; url: string; section: string }[]; } @@ -44,11 +46,13 @@ export const ContentForm: React.FC = ({ className, reportContent, onContentChange, + isUserSentiment, ...rest }) => { const [{ userInfo }] = useApp(); - const [{ impersonate, myTonePool }] = useProfileStore(); - const [{ tonePools }] = useLookup(); + const [{ impersonate, myTonePool }, { storeMyTonePool }] = useProfileStore(); + const [, { getMyTonePool }] = useTonePool(); + const userId = impersonate?.id ?? userInfo?.id ?? 0; const isAV = content?.contentType === ContentTypeName.AudioVideo; const versions = content?.versions?.[userId] ?? { @@ -61,87 +65,68 @@ export const ContentForm: React.FC = ({ : content.summary : content?.body, }; - const [initialValues, setInitialValues] = React.useState({ - tonePools: - content?.tonePools && content?.tonePools.length > 0 - ? content.tonePools.filter((pool: IContentTonePoolModel) => pool.ownerId === userId) - : [ - { - ...myTonePool, - value: - content?.tonePools.find( - (pool: { id: number; ownerId: number }) => - pool.id === myTonePool?.id && pool.ownerId === userId, - )?.value ?? undefined, - }, - ], - }); - const handleSentimentChange = React.useCallback( - async (newTonePool: any) => { - const validTonePool = Array.isArray(newTonePool) ? newTonePool[0] : null; + const userDefaultContentTonePool = { + ...defaultContentTonePool, + ownerId: userId, + name: `${userId}`, + }; - if (!validTonePool) return; + React.useEffect(() => { + let isMounted = true; + const getMyTones = async () => { try { - if (!content?.id) { - console.error('Content is missing required properties'); - return; + const response = await getMyTonePool(userId); + if (isMounted && response) { + storeMyTonePool(response); } + } catch (error) { + console.error('Error fetching tone pool:', error); + } + }; - const existingTonePoolIndex = content?.tonePools?.findIndex( - (tonePool) => tonePool.ownerId === userId, - ); - let updatedContentTonePool; + if (userId && !myTonePool) { + getMyTones(); + } - if (existingTonePoolIndex !== undefined && existingTonePoolIndex >= 0) { - // Replace the existing TonePool value - updatedContentTonePool = content?.tonePools?.map((tonePool, index) => - index === existingTonePoolIndex - ? { ...tonePool, value: validTonePool.value } - : tonePool, - ); - } else { - // Add a new TonePool entry - updatedContentTonePool = [ - ...(content?.tonePools || []), - { ...validTonePool, ownerId: userId }, - ]; - } + // Cleanup function to run when the component unmounts + return () => { + isMounted = false; + }; + }, [getMyTonePool, storeMyTonePool, userId, myTonePool]); - const updatedContent: IContentModel = { - ...content, - tonePools: updatedContentTonePool as IContentTonePoolModel[], - }; + if (!content) return null; - onContentChange?.(updatedContent); - } catch (ex) { - console.error('Error updating tone pools:', ex); - } - }, - [content, onContentChange, userId], - ); + const handleToneSelect = (value: number) => { + const updatedTonePool: IContentTonePoolModel = { + ...myTonePool, + ownerId: + myTonePool?.ownerId !== undefined && myTonePool?.ownerId !== 0 + ? myTonePool.ownerId + : userId, + id: myTonePool?.id ?? 0, + isPublic: myTonePool?.isPublic ?? true, + name: + myTonePool?.name !== undefined && myTonePool?.name !== '' ? myTonePool?.name : `${userId}`, + value, + sortOrder: myTonePool?.sortOrder ?? 0, + isEnabled: myTonePool?.isEnabled ?? true, + description: myTonePool?.description ?? '', + }; - React.useEffect(() => { - const updatedTonePools = - content?.tonePools && content?.tonePools.length > 0 - ? content.tonePools.filter((pool: IContentTonePoolModel) => pool.ownerId === userId) - : [ - { - ...myTonePool, - value: - content?.tonePools.find( - (pool: { id: number; ownerId: number }) => - pool.id === myTonePool?.id && pool.ownerId === userId, - )?.value ?? undefined, - }, - ]; + if (content?.id) { + onContentChange?.({ + ...content, + tonePools: [...(content.tonePools || []), updatedTonePool], + }); + } else { + console.error('Content ID is missing or invalid.'); + } + }; - // Update the versions state with the new tonePools - setInitialValues((prevVersions) => ({ - ...prevVersions, - tonePools: updatedTonePools, - })); - }, [content?.tonePools, userId, myTonePool]); + const userContentTonePool: IContentTonePoolModel = + content?.tonePools?.find((pool: IContentTonePoolModel) => pool.ownerId === userId) || + userDefaultContentTonePool; return show === 'none' ? null : ( @@ -155,11 +140,6 @@ export const ContentForm: React.FC = ({ rows={1} disabled={disabled} onChange={(e) => { - if (!content || typeof content.id !== 'number') { - console.error('Content ID is missing or invalid.'); - return; - } - const values = { ...content, versions: { @@ -180,10 +160,6 @@ export const ContentForm: React.FC = ({ label="Byline" disabled={disabled} onChange={(e) => { - if (!content || typeof content.id !== 'number') { - console.error('Content ID is missing or invalid.'); - return; - } const values = { ...content, versions: { @@ -208,10 +184,6 @@ export const ContentForm: React.FC = ({ value={versions.summary} disabled={disabled} onChange={(text) => { - if (!content || typeof content.id !== 'number') { - console.error('Content ID is missing or invalid.'); - return; - } const values = { ...content, versions: { @@ -235,10 +207,6 @@ export const ContentForm: React.FC = ({ disabled={disabled} urlOptions={reportContent} onChange={(text) => { - if (!content || typeof content.id !== 'number') { - console.error('Content ID is missing or invalid.'); - return; - } const values = { ...content, versions: { @@ -253,26 +221,9 @@ export const ContentForm: React.FC = ({ onContentChange?.(values); }} /> - {}} - enableReinitialize={true} - > - {() => ( -
- - - )} -
+ {isUserSentiment && ( + + )} ); diff --git a/app/subscriber/src/features/my-reports/edit/content/stories/ToneSelector.tsx b/app/subscriber/src/features/my-reports/edit/content/stories/ToneSelector.tsx new file mode 100644 index 0000000000..7d5f2e0286 --- /dev/null +++ b/app/subscriber/src/features/my-reports/edit/content/stories/ToneSelector.tsx @@ -0,0 +1,61 @@ +import React from 'react'; +import { Col, IContentTonePoolModel, Row } from 'tno-core'; + +import * as styled from './styled'; + +const toningOptions = [-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5]; + +interface IToneSelectorProps { + label?: string; + myTonePool: IContentTonePoolModel; + onSelect: (value: number) => void; +} + +export const ToneSelector: React.FC = ({ + label = 'Tone', + onSelect, + myTonePool, +}) => { + const facePositive = `${process.env.PUBLIC_URL}/assets/reports/face-positive@2x.png`; + const faceNeutral = `${process.env.PUBLIC_URL}/assets/reports/face-neutral@2x.png`; + const faceNegative = `${process.env.PUBLIC_URL}/assets/reports/face-negative@2x.png`; + const value = myTonePool?.value; + const [active, setActive] = React.useState(value); + + const determineIndicator = (option: number) => { + if (option === 5) { + return {option.toString()}; + } else if (option === 0) { + return {option.toString()}; + } else if (option === -5) { + return {option.toString()}; + } else { + return  ; + } + }; + + const handleToneSelect = (option: number) => { + setActive(option); + onSelect(option); + }; + + return ( + + + + {toningOptions.map((option) => ( + + {determineIndicator(option)} + + + ))} + + + ); +}; diff --git a/app/subscriber/src/features/my-reports/edit/content/stories/styled/ToneSelector.tsx b/app/subscriber/src/features/my-reports/edit/content/stories/styled/ToneSelector.tsx new file mode 100644 index 0000000000..3d2872e368 --- /dev/null +++ b/app/subscriber/src/features/my-reports/edit/content/stories/styled/ToneSelector.tsx @@ -0,0 +1,62 @@ +import styled from 'styled-components'; + +export interface IToneSelectorProps { + required?: boolean; + active?: boolean; +} + +export const ToneSelector = styled.div.attrs(({ required }) => ({ + required, +}))` + display: flex; + flex-direction: column; + + img { + height: 20px; + width: 20px; + align-self: center; + } + + button { + background-color: #f2f2f2; + border-radius: 15%; + border-color: #f2f2f2; + margin: 0 0.25rem; + margin-top: 0.5rem; + box-shadow: 0px 1px 2px rgba(0, 0, 0, 0.2); + min-width: 2rem; + min-height: 2rem; + align-self: center; + &:hover { + cursor: pointer; + } + } + + .blank { + height: 20px; + width: 20px; + } + + .active { + background-color: #007af5; + color: white; + border: none; + min-height: 2rem; + min-width: 2rem; + } + + label { + align-self: flex-start; + font-weight: 600; + :after { + content: '${(props) => (props.required ? ' *' : '')}'; + color: red; + } + } + + .tone-icon { + font-size: 1.4rem; + margin-bottom: 0rem; + margin-left: 0.5rem; + } +`; diff --git a/app/subscriber/src/features/my-reports/edit/content/stories/styled/index.ts b/app/subscriber/src/features/my-reports/edit/content/stories/styled/index.ts index b0dea81ab2..01a565be9c 100644 --- a/app/subscriber/src/features/my-reports/edit/content/stories/styled/index.ts +++ b/app/subscriber/src/features/my-reports/edit/content/stories/styled/index.ts @@ -1,2 +1,3 @@ export * from './ContentActions'; export * from './ReportSections'; +export * from './ToneSelector'; diff --git a/app/subscriber/src/features/my-reports/edit/validation/SentimentFormSchema.ts b/app/subscriber/src/features/my-reports/edit/validation/SentimentFormSchema.ts deleted file mode 100644 index a48f63d476..0000000000 --- a/app/subscriber/src/features/my-reports/edit/validation/SentimentFormSchema.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { array, number, object } from 'yup'; - -export const sentimentFormSchema = object().shape({ - tonePools: array().of( - object().shape({ - value: number().required('Tone value is required'), - }), - ), -}); diff --git a/app/subscriber/src/store/hooks/lookup/useLookup.ts b/app/subscriber/src/store/hooks/lookup/useLookup.ts index c4e3f92b13..e87ae665de 100644 --- a/app/subscriber/src/store/hooks/lookup/useLookup.ts +++ b/app/subscriber/src/store/hooks/lookup/useLookup.ts @@ -25,7 +25,6 @@ import { StorageKeys, useApiSubscriberCache, useApiSubscriberMinisters, - useApiSubscriberTonePools, } from 'tno-core'; import { useAjaxWrapper } from '..'; @@ -45,7 +44,6 @@ export const useLookup = (): [ILookupState, ILookupController] => { const cache = useApiSubscriberCache(); const lookups = useApiLookups(); const ministers = useApiSubscriberMinisters(); - const tonePools = useApiSubscriberTonePools(); const controller = React.useMemo( () => ({ @@ -158,27 +156,13 @@ export const useLookup = (): [ILookupState, ILookupController] => { 'lookup', ); }, - getTonePools: async () => { - return await fetchIfNoneMatch( - StorageKeys.TonePools, - dispatch, - (etag) => tonePools.getTonePools(etag), - (results) => { - const values = results ?? []; - store.storeTonePools(values); - return values; - }, - true, - 'lookup', - ); - }, init: async () => { // TODO: Handle failures await controller.getLookups(); store.storeIsReady(true); }, }), - [cache, dispatch, lookups, ministers, tonePools, store], + [cache, dispatch, lookups, ministers, store], ); return [state, controller]; diff --git a/app/subscriber/src/store/slices/profile/interfaces/IProfileState.ts b/app/subscriber/src/store/slices/profile/interfaces/IProfileState.ts index d1a781251d..dcb0e58511 100644 --- a/app/subscriber/src/store/slices/profile/interfaces/IProfileState.ts +++ b/app/subscriber/src/store/slices/profile/interfaces/IProfileState.ts @@ -31,5 +31,5 @@ export interface IProfileState { reportContent: { [reportId: number]: number[] }; contributors: IContributorModel[]; messages: ISystemMessageModel[]; - myTonePool: ITonePoolModel; + myTonePool: ITonePoolModel | undefined; } diff --git a/app/subscriber/src/store/slices/profile/profileSlice.ts b/app/subscriber/src/store/slices/profile/profileSlice.ts index a9eb0bc45b..cf00fc03ca 100644 --- a/app/subscriber/src/store/slices/profile/profileSlice.ts +++ b/app/subscriber/src/store/slices/profile/profileSlice.ts @@ -29,15 +29,7 @@ export const initialProfileState: IProfileState = { myColleagues: false, myTonePool: false, }, - myTonePool: { - ownerId: 0, - isPublic: false, - id: 0, - name: '', - description: '', - sortOrder: 0, - isEnabled: false, - }, + myTonePool: undefined, }; export const profileSlice = createSlice({ diff --git a/app/subscriber/src/store/slices/profile/useProfileStore.ts b/app/subscriber/src/store/slices/profile/useProfileStore.ts index a330ffbb8e..66c5f0cd93 100644 --- a/app/subscriber/src/store/slices/profile/useProfileStore.ts +++ b/app/subscriber/src/store/slices/profile/useProfileStore.ts @@ -56,7 +56,7 @@ export interface IProfileStore { storeMyMessages: ( messages: ISystemMessageModel[] | ActionDelegate, ) => void; - storeMyTonePool: (tonePool: ITonePoolModel | ActionDelegate) => void; + storeMyTonePool: (tonePool: ITonePoolModel | ActionDelegate) => void; } export const useProfileStore = (): [IProfileState, IProfileStore] => { @@ -141,10 +141,13 @@ export const useProfileStore = (): [IProfileState, IProfileStore] => { dispatch(storeMyMessages(messages(state.messages))); } else dispatch(storeMyMessages(messages)); }, - storeMyTonePool: (tonePool: ITonePoolModel | ActionDelegate) => { + storeMyTonePool: (tonePool: ITonePoolModel | ActionDelegate) => { if (typeof tonePool === 'function') { - dispatch(storeMyTonePool(tonePool(state.myTonePool))); - } else { + const updatedTonePool = tonePool(state.myTonePool); + if (updatedTonePool !== undefined) { + dispatch(storeMyTonePool(updatedTonePool)); + } + } else if (tonePool !== undefined) { dispatch(storeMyTonePool(tonePool)); } }, diff --git a/libs/net/dal/Extensions/ContentExtensions.cs b/libs/net/dal/Extensions/ContentExtensions.cs index 8de22086c6..7a7f8b75c1 100644 --- a/libs/net/dal/Extensions/ContentExtensions.cs +++ b/libs/net/dal/Extensions/ContentExtensions.cs @@ -163,33 +163,29 @@ public static TNOContext UpdateContext(this TNOContext context, Content original } }); - var tonePoolsToAdd = new List(); - - oTonePools.Except(updated.TonePoolsManyToMany).ForEach(a => - { - context.Entry(a).State = EntityState.Deleted; - }); updated.TonePoolsManyToMany.ForEach(a => { var current = a.TonePoolId != 0 ? oTonePools.FirstOrDefault(o => o.TonePoolId == a.TonePoolId) : null; + + // If no matching tone pool exists, add it to the original list if (current == null) { a.TonePool ??= context.TonePools.FirstOrDefault(tp => tp.Id == a.TonePoolId); - tonePoolsToAdd.Add(a); + + if (a.TonePool != null && + !original.TonePoolsManyToMany.Any(x => x.TonePool != null && x.TonePool.Id == a.TonePool.Id)) + { + original.TonePoolsManyToMany.Add(a); + } } + // If a matching tone pool is found but the values have changed, update the values else if (current.Value != a.Value) { current.Value = a.Value; current.Version = a.Version; } }); - tonePoolsToAdd.ForEach(a => { - if (a.TonePool != null && - !original.TonePoolsManyToMany.Any(x => x.TonePool != null && x.TonePool.Id == a.TonePool.Id)) - { - original.TonePoolsManyToMany.Add(a); - } - }); + oTimeTrackings.Except(updated.TimeTrackings).ForEach(a => { context.Entry(a).State = EntityState.Deleted; diff --git a/libs/net/dal/Services/TonePoolService.cs b/libs/net/dal/Services/TonePoolService.cs index 73bdbc0efd..3e6a815e85 100644 --- a/libs/net/dal/Services/TonePoolService.cs +++ b/libs/net/dal/Services/TonePoolService.cs @@ -27,7 +27,7 @@ public IEnumerable FindAll() public TonePool? FindByUserId(int userId) { - return this.Context.TonePools.FirstOrDefault(tp => tp.OwnerId == userId && tp.IsPublic == true); + return this.Context.TonePools.FirstOrDefault(tp => tp.OwnerId == userId); } public override TonePool AddAndSave(TonePool entity) diff --git a/libs/net/entities/Models/ContentVersion.cs b/libs/net/entities/Models/ContentVersion.cs index 8d70790fdd..2dacc73e7e 100644 --- a/libs/net/entities/Models/ContentVersion.cs +++ b/libs/net/entities/Models/ContentVersion.cs @@ -56,10 +56,6 @@ public class ContentVersion /// public bool IsPrivate { get; set; } = false; - /// - /// get/set - Tone for the content - /// - public int? Tone {get;set;} #endregion @@ -86,7 +82,6 @@ public ContentVersion(Content content, User owner) this.Body = content.Body; this.SourceUrl = content.SourceUrl; this.IsPrivate = content.IsPrivate; - this.Tone = content.TonePools.Count > 0 ? (int?)content.TonePools[0].Id : null; } /// @@ -98,4 +93,4 @@ public ContentVersion(int ownerId) this.OwnerId = ownerId; } #endregion -} +} \ No newline at end of file diff --git a/libs/npm/core/src/components/formik/sentiment/FormikSentiment.tsx b/libs/npm/core/src/components/formik/sentiment/FormikSentiment.tsx index de48a6e652..359488dee0 100644 --- a/libs/npm/core/src/components/formik/sentiment/FormikSentiment.tsx +++ b/libs/npm/core/src/components/formik/sentiment/FormikSentiment.tsx @@ -1,12 +1,11 @@ import { getIn, useFormikContext } from 'formik'; import React from 'react'; -import { FaFrown, FaMeh, FaSmile } from 'react-icons/fa'; import FaceFrownOpen from '../../../assets/face-frown-open.svg'; import FaceGrinWide from '../../../assets/face-grin-wide.svg'; import FaceMeh from '../../../assets/face-meh.svg'; import { Col, Error, Row } from '../../../components'; -import { IContentTonePoolModel, ITonePoolModel } from '../../../hooks'; +import { ITonePoolModel } from '../../../hooks'; import * as styled from './styled'; const toningOptions = [-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5]; @@ -18,14 +17,10 @@ export interface IFormikSentimentProps { label?: string; /** Sentiment options. */ options: ITonePoolModel[]; - /** Sentiment icon colored */ - coloredIcon?: boolean; /** The name of the default tone pool */ defaultTonePoolName?: string; /** The id of the default tone pool */ defaultTonePoolId?: number; - /** Update upper form */ - onSentimentChange?: (tonePools: any) => void; /** Whether this field is required. */ required?: boolean; } @@ -39,10 +34,8 @@ export const FormikSentiment = ({ name, label = 'Sentiment', options, - coloredIcon = false, defaultTonePoolId, defaultTonePoolName = 'Default', - onSentimentChange, ...rest }: IFormikSentimentProps) => { const { values, setFieldValue, touched, errors } = useFormikContext(); @@ -51,18 +44,14 @@ export const FormikSentiment = ({ name, label, options, - coloredIcon, defaultTonePoolId, defaultTonePoolName, - onSentimentChange, ...rest, }; const defaultTonePool = options.find( (t) => t.id === defaultTonePoolId || t.name === defaultTonePoolName, ); - - const tonePools = getIn(values, name.toString()) as IContentTonePoolModel[] | undefined; - + const tonePools = getIn(values, name.toString()); const value = tonePools && Array.isArray(tonePools) && tonePools.length ? tonePools[0].value : undefined; @@ -75,65 +64,40 @@ export const FormikSentiment = ({ if (active !== value) { setActive(value); } - }, [value, active]); + }, [active, value]); const determineIndicator = (option: number) => { if (option === 5) { - return coloredIcon ? ( - - ) : ( - {option.toString()} - ); + return {option.toString()}; } else if (option === 0) { - return coloredIcon ? ( - - ) : ( - {option.toString()} - ); + return {option.toString()}; } else if (option === -5) { - return coloredIcon ? ( - - ) : ( - {option.toString()} - ); + return {option.toString()}; } else { return  ; } }; - const handleOptionClick = (option: number) => { - if (!defaultTonePool) return; - - setActive(option); - - const existingTonePoolIndex = tonePools?.findIndex((pool) => pool.id === defaultTonePool.id); - let updatedTonePools; - - if (existingTonePoolIndex !== undefined && existingTonePoolIndex >= 0) { - // Replace the existing TonePool value - updatedTonePools = tonePools?.map((pool, index) => - index === existingTonePoolIndex ? { ...pool, value: option } : pool, - ); - } else { - // Add a new TonePool entry - updatedTonePools = [...(tonePools || []), { ...defaultTonePool, value: option }]; - } - - setFieldValue(name.toString(), updatedTonePools, true); - onSentimentChange?.(updatedTonePools); - }; - return ( + {' '} {toningOptions.map((option) => ( {determineIndicator(option)} diff --git a/libs/npm/core/src/components/formik/sentiment/styled/FormikSentiment.tsx b/libs/npm/core/src/components/formik/sentiment/styled/FormikSentiment.tsx index fe2288be35..4826efdf02 100644 --- a/libs/npm/core/src/components/formik/sentiment/styled/FormikSentiment.tsx +++ b/libs/npm/core/src/components/formik/sentiment/styled/FormikSentiment.tsx @@ -50,10 +50,4 @@ export const FormikSentiment = styled.div.attrs>(({ r color: red; } } - - .tone-icon { - font-size: 1.4rem; - margin-bottom: 0rem; - margin-left: 0.5rem; - } `; diff --git a/libs/npm/core/src/hooks/api/interfaces/IContentTonePoolModel.ts b/libs/npm/core/src/hooks/api/interfaces/IContentTonePoolModel.ts index 72bd8cf14e..c0fdf69046 100644 --- a/libs/npm/core/src/hooks/api/interfaces/IContentTonePoolModel.ts +++ b/libs/npm/core/src/hooks/api/interfaces/IContentTonePoolModel.ts @@ -1,5 +1,5 @@ import { ITonePoolModel } from '.'; export interface IContentTonePoolModel extends ITonePoolModel { - value: number; + value: number | undefined; } diff --git a/libs/npm/core/src/hooks/api/interfaces/IContentVersionModel.ts b/libs/npm/core/src/hooks/api/interfaces/IContentVersionModel.ts index a83a24618e..c4d79510ce 100644 --- a/libs/npm/core/src/hooks/api/interfaces/IContentVersionModel.ts +++ b/libs/npm/core/src/hooks/api/interfaces/IContentVersionModel.ts @@ -9,5 +9,4 @@ export interface IContentVersionModel extends IAuditColumnsModel { page?: string; summary?: string; body?: string; - tone?: number; } diff --git a/libs/npm/core/src/hooks/api/interfaces/ITonePool.ts b/libs/npm/core/src/hooks/api/interfaces/ITonePool.ts new file mode 100644 index 0000000000..3c91975b4a --- /dev/null +++ b/libs/npm/core/src/hooks/api/interfaces/ITonePool.ts @@ -0,0 +1,5 @@ +export interface ITonePool { + id: number; + name: string; + value?: number; +} diff --git a/libs/npm/core/src/hooks/api/interfaces/index.ts b/libs/npm/core/src/hooks/api/interfaces/index.ts index 8879a3b5cb..f68c669cf9 100644 --- a/libs/npm/core/src/hooks/api/interfaces/index.ts +++ b/libs/npm/core/src/hooks/api/interfaces/index.ts @@ -106,6 +106,7 @@ export * from './ISystemMessageModel'; export * from './ITagFilter'; export * from './ITagModel'; export * from './ITimeTrackingModel'; +export * from './ITonePool'; export * from './ITonePoolModel'; export * from './ITopicFilter'; export * from './ITopicModel'; diff --git a/libs/npm/core/src/hooks/api/subscriber/useApiSubscriberTonePools.ts b/libs/npm/core/src/hooks/api/subscriber/useApiSubscriberTonePools.ts index c9114ebd75..cb4af9f2c0 100644 --- a/libs/npm/core/src/hooks/api/subscriber/useApiSubscriberTonePools.ts +++ b/libs/npm/core/src/hooks/api/subscriber/useApiSubscriberTonePools.ts @@ -28,7 +28,6 @@ export const useApiSubscriberTonePools = ( ); }, getMyTonePool: (userId: number) => { - console.log('getMyTonePool'); return api.get, any>( `/subscriber/tonePool/user/${userId}`, ); diff --git a/libs/npm/core/tsconfig.json b/libs/npm/core/tsconfig.json index 99314a1ff8..adf8c04df1 100644 --- a/libs/npm/core/tsconfig.json +++ b/libs/npm/core/tsconfig.json @@ -7,10 +7,11 @@ "declaration": true, "declarationMap": true, "emitDeclarationOnly": false, - "lib": ["dom", "dom.iterable", "esnext"], - "paths": { - "@assets/*": ["../../../../app/subscriber/public/assets/*"] - }, + "lib": [ + "dom", + "dom.iterable", + "esnext" + ], "allowJs": true, "skipLibCheck": true, "esModuleInterop": true, @@ -26,8 +27,13 @@ "inlineSources": true, "isolatedModules": true, "jsx": "react-jsx", - "typeRoots": ["src/@types", "node_modules/@types"] + "typeRoots": [ + "src/@types", + "node_modules/@types" + ] }, - "include": ["src"], + "include": [ + "src" + ], "exclude": ["node_modules", "dist"] -} +} \ No newline at end of file diff --git a/libs/npm/core/webpack.config.js b/libs/npm/core/webpack.config.js index bd15ff9f6d..3b435267fa 100644 --- a/libs/npm/core/webpack.config.js +++ b/libs/npm/core/webpack.config.js @@ -12,9 +12,6 @@ module.exports = { ], }, resolve: { - alias: { - '@assets': path.resolve(__dirname, '../../../../app/subscriber/public/assets'), - }, extensions: ['.tsx', '.ts', '.js'], }, output: { @@ -24,9 +21,3 @@ module.exports = { }, devtool: 'source-map', }; - -// Add this to print the resolved alias path -console.log( - 'Resolved @assets path:', - path.resolve(__dirname, '../../../../app/subscriber/public/assets'), -);