From 705128c1de81a06b81d30a66ac9fd8987fcc8a29 Mon Sep 17 00:00:00 2001 From: johnservo Date: Mon, 27 Mar 2023 16:15:48 +0800 Subject: [PATCH 1/8] Refactor main logo code --- backend/models/core/Setting.js | 41 ++++- components/elements/frontPage/LoginForm.js | 4 +- pages/admin/settings.js | 202 +++++++++------------ pages/index.js | 50 ++--- 4 files changed, 150 insertions(+), 147 deletions(-) diff --git a/backend/models/core/Setting.js b/backend/models/core/Setting.js index b71f45a6..cc834d52 100644 --- a/backend/models/core/Setting.js +++ b/backend/models/core/Setting.js @@ -1,21 +1,44 @@ -import DB from '@klaudsol/commons/lib/DB'; +import DB from "@klaudsol/commons/lib/DB"; class Resource { - static async get({ slug }) { + static async get() { const db = new DB(); - const getSettingSQL = `SELECT * FROM \`settings\` WHERE \`key\` = :key`; + const sql = `SELECT * FROM \`settings\``; + + const data = await db.executeStatement(sql); + const output = data.records.map( + ([ + { longValue: id }, + { stringValue: setting }, + { stringValue: value }, + ]) => ({ + id, + setting, + value, + }) + ); - const resource = await db.executeStatement(getSettingSQL, [ - { name: "key", value: { stringValue: slug } }, - ]); + return output; + } - return resource.records.map( - ([{ longValue: id }, { stringValue: key }, { stringValue: value }]) => ({ + static async getLogo() { + const db = new DB(); + const sql = `SELECT * FROM settings WHERE setting = "mainlogo"`; + + const data = await db.executeStatement(sql); + const output = data.records.map( + ([ + { longValue: id }, + { stringValue: setting }, + { stringValue: value }, + ]) => ({ id, - key, + setting, value, }) ); + + return output; } static async create({ key, value }) { diff --git a/components/elements/frontPage/LoginForm.js b/components/elements/frontPage/LoginForm.js index b05f847b..37b60140 100644 --- a/components/elements/frontPage/LoginForm.js +++ b/components/elements/frontPage/LoginForm.js @@ -67,8 +67,8 @@ const LoginForm = ({ className, logo }) => {
cms-logo { - const initialVal = Object.keys(data).length !== 0 - ? { mainlogo: { name: data.key, link: data.link, key: data.value } } - : {}; - return initialVal; - }; - - useEffect(() => { - (async () => { - try { - const response = await slsFetch("/api/settings/mainlogo"); - const { data } = await response.json(); - const newData = setInitialValues(data); + /* const setInitialValues = (data) => { */ + /* const initialVal = Object.keys(data).length !== 0 */ + /* ? { mainlogo: { name: data.key, link: data.link, key: data.value } } */ + /* : {}; */ + /* return initialVal; */ + /* }; */ - dispatch({ type: SET_VALUES, payload: newData }); - } catch (error) { - dispatch({ type: SET_ERROR, payload: error.message }); - } finally { - dispatch({ type: CLEANUP }); - } - })(); - }, []); + /* useEffect(() => { */ + /* (async () => { */ + /* try { */ + /* const response = await slsFetch("/api/settings/mainlogo"); */ + /* const { data } = await response.json(); */ + /* const newData = setInitialValues(data); */ + /**/ + /* dispatch({ type: SET_VALUES, payload: newData }); */ + /* } catch (error) { */ + /* dispatch({ type: SET_ERROR, payload: error.message }); */ + /* } finally { */ + /* dispatch({ type: CLEANUP }); */ + /* } */ + /* })(); */ + /* }, []); */ - const onDelete = useCallback((setStaticLink) => { - (async () => { - try { - dispatch({ type: DELETING, payload: true }); - const response = await slsFetch(`/api/settings/mainlogo`, { - method: "DELETE", - headers: { - "Content-type": "application/json", - }, - }); - dispatch({ type: SET_VALUES, payload: {} }); - formRef.current.resetForm({ values: {} }); - setStaticLink(''); - dispatch({ type: SET_CHANGED, payload:false }) - } catch (ex) { - console.error(ex); - } finally { - dispatch({ type: DELETING, payload: false }); - } - })(); - }, []); + /* const onDelete = useCallback((setStaticLink) => { */ + /* (async () => { */ + /* try { */ + /* dispatch({ type: DELETING, payload: true }); */ + /* const response = await slsFetch(`/api/settings/mainlogo`, { */ + /* method: "DELETE", */ + /* headers: { */ + /* "Content-type": "application/json", */ + /* }, */ + /* }); */ + /* dispatch({ type: SET_VALUES, payload: {} }); */ + /* formRef.current.resetForm({ values: {} }); */ + /* setStaticLink(''); */ + /* dispatch({ type: SET_CHANGED, payload:false }) */ + /* } catch (ex) { */ + /* console.error(ex); */ + /* } finally { */ + /* dispatch({ type: DELETING, payload: false }); */ + /* } */ + /* })(); */ + /* }, []); */ const onSubmit = (evt) => { evt.preventDefault(); formRef.current.handleSubmit(); }; - const getS3Keys = (files) => { - if(!files) return - - const fileKeys = Object.keys(files); - const s3Keys = fileKeys.map((file) => state.values[file].key); - - return s3Keys; - }; + /* const getS3Keys = (files) => { */ + /* if(!files) return */ + /**/ + /* const fileKeys = Object.keys(files); */ + /* const s3Keys = fileKeys.map((file) => state.values[file].key); */ + /* */ + /* return s3Keys; */ + /* }; */ const formikParams = { innerRef: formRef, @@ -94,25 +102,7 @@ export default function Settings({ cache }) { (async () => { try { dispatch({ type: SAVING }); - const isFile = Object.entries(values)[0][1] instanceof File; - const isCreateMode = !isValueExists && isFile; - - const filesToUpload = !isCreateMode && getAllFiles(values); - const s3Keys = getS3Keys(filesToUpload); - const newValues = isCreateMode ? values : {...values, toDeleteRaw: s3Keys} - - const formattedEntries = convertToFormData(newValues); - - const response = await slsFetch(`/api/settings${isCreateMode ? '' : '/mainlogo'}`, { - method: `${isCreateMode ? "POST" : "PUT"}`, - body: formattedEntries, - }); - const { data } = await response.json() - - const newData = setInitialValues(data); - dispatch({ type: SET_VALUES, payload: newData }); - formRef.current.resetForm({ values: newData }); - dispatch({ type: SET_CHANGED, payload:false }) + console.log(values); } catch (ex) { console.error(ex); } finally { @@ -127,47 +117,35 @@ export default function Settings({ cache }) {
- {capabilities.includes(readSettings) ?
-
-
-

Settings

-
-
- {capabilities.includes(modifyLogo) && : } - onClick={onSubmit} - isDisabled={state.isLoading || state.isSaving || state.isDeleting || !state.isChanged} - />} -
+
+
+

Settings

+ : } + onClick={onSubmit} + />
- {!state.isLoading && ( - - {() => ( -
- - - )} -
- )} - {!isValueExists && !state.isLoading && "Logo is not set"} -
:

{state.errorMessage}

} + +
+
+ Default view + + + + +
+
+ CMS Name + +
+
+ Logo + +
+
+
+
diff --git a/pages/index.js b/pages/index.js index d70ce767..e64ef0fd 100644 --- a/pages/index.js +++ b/pages/index.js @@ -13,33 +13,35 @@ export default function Index(props) { export const getServerSideProps = withIronSessionSsr( async function getServerSideProps({ req, res }) { - try { - var rawData = await Setting.get({ slug: mainlogo }); - var data = { ...rawData[0], link:`${process.env.KS_S3_BASE_URL}/${rawData[0].value}` }; - // To ensure maximum efficiency and avoid any unnecessary involvement with the assertUserCan() function, - // it is recommended to directly fetch our logo from the core setting. - } catch (err) {} + const rawData = await Setting.getLogo(); + const data = rawData[0]; - try{ - if(req.session?.cache?.forcePasswordChange){ - await serverSideLogout(req) - } - } catch(err){} + if (req.session?.cache?.forcePasswordChange) { + await serverSideLogout(req); + } + + const homepage = req.session?.cache?.homepage; + if (homepage && !req.session?.cache?.forcePasswordChange) { + return { + redirect: { + permanent: false, + destination: `/${homepage}`, + }, + }; + } else { + const logoLink = `${process.env.KS_S3_BASE_URL}/${data.value}`; + + return { + props: { + logo: data.value !== "default" ? logoLink : "/logo-180x180.png", + }, + }; + } + } catch (err) { + console.error(err); - let homepage = req.session?.cache?.homepage; - if ((homepage && !req.session?.cache?.forcePasswordChange)) { - return { - redirect: { - permanent: false, - destination: `/${homepage}`, - }, - }; - } - else { - return { - props: { logo: (process.env.KS_S3_BASE_URL && rawData?.length) ? data : {} }, - }; + return { props: {} }; } }, sessionOptions From 1882001403e3ca76d87421f74bb7fba0e310d50c Mon Sep 17 00:00:00 2001 From: johnservo Date: Mon, 27 Mar 2023 17:11:02 +0800 Subject: [PATCH 2/8] Can now receive settings from db --- components/fields/FileField.js | 4 +- components/reducers/settingReducer.js | 8 ++- pages/admin/settings.js | 74 ++++++++------------------- pages/api/settings.js | 25 ++++++++- 4 files changed, 52 insertions(+), 59 deletions(-) diff --git a/components/fields/FileField.js b/components/fields/FileField.js index 13e66a6b..7022e613 100644 --- a/components/fields/FileField.js +++ b/components/fields/FileField.js @@ -13,7 +13,7 @@ const FileField = (props) => { const { onChange, value, ...formattedField } = field; - const [staticLink,setStaticLink] = useState(''); + const [staticLink,setStaticLink] = useState('/'); const inputRef = useRef(); const isFetching = props.isSaving || props.isDeleting; @@ -94,7 +94,7 @@ const FileField = (props) => { {(value || staticLink) && ( {value?.name} { default: return state } -}; \ No newline at end of file +}; diff --git a/pages/admin/settings.js b/pages/admin/settings.js index 632b5d1f..b6ac641c 100644 --- a/pages/admin/settings.js +++ b/pages/admin/settings.js @@ -36,68 +36,34 @@ export default function Settings({ cache }) { const isValueExists = Object.keys(state.values).length !== 0; const capabilities = cache?.capabilities; - /* const setInitialValues = (data) => { */ - /* const initialVal = Object.keys(data).length !== 0 */ - /* ? { mainlogo: { name: data.key, link: data.link, key: data.value } } */ - /* : {}; */ - /* return initialVal; */ - /* }; */ + useEffect(() => { + (async () => { + try { + const response = await slsFetch("/api/settings/"); + const { data: dataRaw } = await response.json(); - /* useEffect(() => { */ - /* (async () => { */ - /* try { */ - /* const response = await slsFetch("/api/settings/mainlogo"); */ - /* const { data } = await response.json(); */ - /* const newData = setInitialValues(data); */ - /**/ - /* dispatch({ type: SET_VALUES, payload: newData }); */ - /* } catch (error) { */ - /* dispatch({ type: SET_ERROR, payload: error.message }); */ - /* } finally { */ - /* dispatch({ type: CLEANUP }); */ - /* } */ - /* })(); */ - /* }, []); */ + const data = dataRaw.reduce((acc, curr) => { + return { ...acc, [curr.setting]: curr.value }; + }, {}); - /* const onDelete = useCallback((setStaticLink) => { */ - /* (async () => { */ - /* try { */ - /* dispatch({ type: DELETING, payload: true }); */ - /* const response = await slsFetch(`/api/settings/mainlogo`, { */ - /* method: "DELETE", */ - /* headers: { */ - /* "Content-type": "application/json", */ - /* }, */ - /* }); */ - /* dispatch({ type: SET_VALUES, payload: {} }); */ - /* formRef.current.resetForm({ values: {} }); */ - /* setStaticLink(''); */ - /* dispatch({ type: SET_CHANGED, payload:false }) */ - /* } catch (ex) { */ - /* console.error(ex); */ - /* } finally { */ - /* dispatch({ type: DELETING, payload: false }); */ - /* } */ - /* })(); */ - /* }, []); */ + dispatch({ type: SET_VALUES, payload: data }); + } catch (error) { + dispatch({ type: SET_ERROR, payload: error.message }); + } finally { + dispatch({ type: CLEANUP }); + } + })(); + }, []); const onSubmit = (evt) => { evt.preventDefault(); formRef.current.handleSubmit(); }; - /* const getS3Keys = (files) => { */ - /* if(!files) return */ - /**/ - /* const fileKeys = Object.keys(files); */ - /* const s3Keys = fileKeys.map((file) => state.values[file].key); */ - /* */ - /* return s3Keys; */ - /* }; */ - const formikParams = { innerRef: formRef, initialValues: state.values, + enableReinitialize: true, onSubmit: (values) => { (async () => { try { @@ -130,18 +96,18 @@ export default function Settings({ cache }) {
Default view - +
CMS Name - +
Logo - +
diff --git a/pages/api/settings.js b/pages/api/settings.js index 1deb080e..ed263654 100644 --- a/pages/api/settings.js +++ b/pages/api/settings.js @@ -41,7 +41,30 @@ async function handler(req, res) { } async function get(req, res) { - + await assertUserCan(readSettings, req); + + const settings = await Setting.get(); + + // Formats the values for the `main_logo` property since its an image from S3 + const mainLogoIndex = settings.findIndex((item) => item.setting === 'main_logo'); + const mainLogo = settings[mainLogoIndex]; + const mainLogoValue = mainLogo.value; + mainLogo.value = { + name: mainLogoValue.substring(mainLogoValue.indexOf('_') + 1), + key: mainLogoValue, + link: `${process.env.KS_S3_BASE_URL}/${mainLogoValue}` + } + settings[mainLogoIndex] = mainLogo; + + const output = { + data: settings, + metadata: {} + } + + output.metadata.hash = createHash(output); + + setCORSHeaders({response: res, url: process.env.FRONTEND_URL}); + settings ? res.status(OK).json(output ?? []) : res.status(NOT_FOUND).json({}) } async function create(req, res) { From e9ed76ec3277f584f4c51b98b31180d91f59ab2f Mon Sep 17 00:00:00 2001 From: johnservo Date: Mon, 27 Mar 2023 17:47:37 +0800 Subject: [PATCH 3/8] Connected backend and frontend --- backend/models/core/Setting.js | 30 ++++-------- .../[entity_type_slug]/[id].js | 2 +- pages/admin/settings.js | 49 +++++++++++++------ pages/api/settings.js | 26 +++++++--- 4 files changed, 65 insertions(+), 42 deletions(-) diff --git a/backend/models/core/Setting.js b/backend/models/core/Setting.js index cc834d52..bb6fe7dc 100644 --- a/backend/models/core/Setting.js +++ b/backend/models/core/Setting.js @@ -80,32 +80,22 @@ class Resource { } } - static async update({ key, value }) { + static async update(entry) { const db = new DB(); - const updateValuesBatchSQL = `UPDATE settings SET - value = :value - WHERE \`key\` = :key + const sql = ` + UPDATE settings SET value = :value + WHERE \`setting\` = :setting `; - const valueParams = [ - { name: "key", value: { stringValue: key } }, - { name: "value", value: { stringValue: value } }, - ]; - await db.executeStatement(updateValuesBatchSQL, valueParams); + const valueParams = Object.keys(entry).map((e) => [ + { name: 'setting', value: { stringValue: e }}, + { name: 'value', value: { stringValue: entry[e] }} + ]) - const getSettingSQL = `SELECT * FROM \`settings\` WHERE \`key\` = :key`; - const updatedResource = await db.executeStatement(getSettingSQL, [ - { name: "key", value: { stringValue: key } }, - ]); + await db.batchExecuteStatement(sql, valueParams); - return updatedResource.records.map( - ([{ longValue: id }, { stringValue: key }, { stringValue: value }]) => ({ - id, - key, - value, - }) - ); + return true; } static async delete({ slug }) { diff --git a/pages/admin/content-manager/[entity_type_slug]/[id].js b/pages/admin/content-manager/[entity_type_slug]/[id].js index 5029980a..27c6f0a7 100644 --- a/pages/admin/content-manager/[entity_type_slug]/[id].js +++ b/pages/admin/content-manager/[entity_type_slug]/[id].js @@ -154,7 +154,7 @@ export default function Type({ cache }) { body: JSON.stringify(entry), }); - const { message, presignedUrls } = await response.json(); + const { presignedUrls } = await response.json(); if (files.length > 0) await uploadFilesToUrl(files, presignedUrls); diff --git a/pages/admin/settings.js b/pages/admin/settings.js index b6ac641c..e26a7605 100644 --- a/pages/admin/settings.js +++ b/pages/admin/settings.js @@ -6,28 +6,16 @@ import { useRef, useReducer, useEffect, useState, useCallback } from "react"; import AppButtonLg from "@/components/klaudsolcms/buttons/AppButtonLg"; import AppButtonSpinner from "@/components/klaudsolcms/AppButtonSpinner"; -import { sortByOrderAsc } from "@/components/Util"; import { slsFetch } from "@klaudsol/commons/lib/Client"; /** react-icons */ -import { FaCheck, FaImage, FaTrash } from "react-icons/fa"; +import { FaCheck } from "react-icons/fa"; import UploadRenderer from "@/components/renderers/admin/UploadRenderer"; import { settingReducer, initialState, } from "@/components/reducers/settingReducer"; -import { - SAVING, - LOADING, - DELETING, - CLEANUP, - SET_VALUES, - SET_CHANGED, - SET_ERROR, -} from "@/lib/actions"; -import { defaultLogo } from "@/constants/index"; -import { convertToFormData, getAllFiles } from "@/lib/s3FormController"; -import { validImageTypes } from "@/lib/Constants"; +import { SAVING, CLEANUP, SET_VALUES, SET_ERROR } from "@/lib/actions"; import { readSettings, modifyLogo } from "@/lib/Constants"; export default function Settings({ cache }) { @@ -60,6 +48,15 @@ export default function Settings({ cache }) { formRef.current.handleSubmit(); }; + const getFilesToDelete = (values) => { + const files = Object.keys(values).filter( + (value) => values[value] instanceof File + ); + const keys = files.map((file) => state.values[file].key); + + return keys; + }; + const formikParams = { innerRef: formRef, initialValues: state.values, @@ -68,7 +65,29 @@ export default function Settings({ cache }) { (async () => { try { dispatch({ type: SAVING }); - console.log(values); + const { files, data, fileNames } = await getBody(values); + const toDelete = getFilesToDelete(values); + + const entry = { + ...data, + fileNames, + toDelete, + }; + + const url = `/api/settings`; + const params = { + method: "PUT", + headers: { + "Content-type": "application/json", + }, + body: JSON.stringify(entry), + }; + + const response = await slsFetch(url, params); + + const { presignedUrls } = await response.json(); + + if (files.length > 0) await uploadFilesToUrl(files, presignedUrls); } catch (ex) { console.error(ex); } finally { diff --git a/pages/api/settings.js b/pages/api/settings.js index ed263654..eee3aaa6 100644 --- a/pages/api/settings.js +++ b/pages/api/settings.js @@ -3,6 +3,7 @@ import { withSession } from '@klaudsol/commons/lib/Session'; import { defaultErrorHandler } from '@klaudsol/commons/lib/ErrorHandler'; import { generateResource, + generatePresignedUrls, addFileToBucket } from "@/backend/data_access/S3"; @@ -17,12 +18,6 @@ import { readSettings, writeSettings } from '@/lib/Constants'; export default withSession(handler); -export const config = { - api: { - bodyParser: false, - }, -} - async function handler(req, res) { try { @@ -32,6 +27,8 @@ async function handler(req, res) { case "POST": const { req: parsedReq, res: parsedRes } = await parseFormData(req, res); return await create(parsedReq, parsedRes); + case "PUT": + return update(req, res); default: throw new Error(`Unsupported method: ${req.method}`); } @@ -67,6 +64,7 @@ async function get(req, res) { settings ? res.status(OK).json(output ?? []) : res.status(NOT_FOUND).json({}) } +// deprecated async function create(req, res) { try { await assert({ @@ -120,3 +118,19 @@ async function create(req, res) { } } + +async function update(req, res) { + await assert({ + loggedIn: true, + }, req); + + await assertUserCan(writeSettings, req); + + const { fileNames, toDelete, ...entry } = req.body; + await Setting.update(entry); + + const presignedUrls = fileNames.length > 0 && await generatePresignedUrls(fileNames); + + res.status(OK).json({ message: 'Successfully updated the settings', presignedUrls }) +} + From 40bd7a47561676b5190b6fb83a320c5c2b6eebc8 Mon Sep 17 00:00:00 2001 From: johnservo Date: Mon, 27 Mar 2023 18:19:54 +0800 Subject: [PATCH 4/8] Transferred settings to rootState --- components/elements/inner/AppSidebar.js | 3 +- components/fields/FileField.js | 5 ++- components/reducers/actions.js | 20 +++++++++- components/reducers/rootReducer.js | 14 ++++++- lib/actions.js | 1 + pages/admin/settings.js | 49 +++++++++++++------------ 6 files changed, 64 insertions(+), 28 deletions(-) diff --git a/components/elements/inner/AppSidebar.js b/components/elements/inner/AppSidebar.js index 4e7123c5..65cad477 100644 --- a/components/elements/inner/AppSidebar.js +++ b/components/elements/inner/AppSidebar.js @@ -17,7 +17,7 @@ import { SET_COLLAPSE } from '@/lib/actions'; import RootContext from '@/components/contexts/RootContext'; import { useCapabilities } from '@/components/hooks'; import { writeSettings, writeContentTypes, readUsers, readGroups } from "@/lib/Constants"; -import { loadEntityTypes } from '@/components/reducers/actions'; +import { loadEntityTypes, loadSettings } from '@/components/reducers/actions'; const AppSidebar = () => { @@ -98,6 +98,7 @@ const AppSidebar = () => { useEffect(() => { (async () => { await loadEntityTypes({rootState, rootDispatch}); + await loadSettings({ rootState, rootDispatch }); })(); }, [rootState]); diff --git a/components/fields/FileField.js b/components/fields/FileField.js index 7022e613..a9b06084 100644 --- a/components/fields/FileField.js +++ b/components/fields/FileField.js @@ -6,6 +6,7 @@ import AppButtonSpinner from "@/components/klaudsolcms/AppButtonSpinner"; import { FaTrash } from "react-icons/fa"; import { useEffect } from "react"; import { SET_CHANGED } from "@/lib/actions" +import defaultImage from "@/public/default-image.svg"; const FileField = (props) => { const { setFieldValue, setTouched, touched } = useFormikContext(); @@ -13,7 +14,7 @@ const FileField = (props) => { const { onChange, value, ...formattedField } = field; - const [staticLink,setStaticLink] = useState('/'); + const [staticLink,setStaticLink] = useState(''); const inputRef = useRef(); const isFetching = props.isSaving || props.isDeleting; @@ -93,7 +94,7 @@ const FileField = (props) => {
{(value || staticLink) && ( {value?.name { + return { ...acc, [curr.setting]: curr.value }; + }, {}); + + rootDispatch({ + type: SET_SETTINGS, + payload: data + }); + } catch (ex) { + console.error(ex.stack); + } +} + const hashEqualTo = ({ rootState, typeSlug, hash }) => rootState.entityType[typeSlug]?.metadata?.hash === hash; diff --git a/components/reducers/rootReducer.js b/components/reducers/rootReducer.js index 2df0f7e8..43de9d50 100644 --- a/components/reducers/rootReducer.js +++ b/components/reducers/rootReducer.js @@ -3,10 +3,16 @@ import { RESET_CLIENT_SESSION, SET_ENTITY_TYPES, SET_COLLAPSE, - SET_CURRENT_ENTITY_TYPE + SET_CURRENT_ENTITY_TYPE, + SET_SETTINGS } from '@/lib/actions'; export const rootInitialState = { + settings: { + default_view: '', + cms_name: '', + main_logo: '' + }, entityTypes: [], entityType: {}, currentContentType:{} @@ -56,6 +62,12 @@ export const rootReducer = (state, action) => { ...state, currentContentType: action.payload } + + case SET_SETTINGS: + return { + ...state, + settings: action.payload + } default: return state; diff --git a/lib/actions.js b/lib/actions.js index a98dcb2c..8f894558 100644 --- a/lib/actions.js +++ b/lib/actions.js @@ -4,6 +4,7 @@ export const SET_CLIENT_SESSION = "SET_CLIENT_SESSION"; export const RESET_CLIENT_SESSION = "RESET_CLIENT_SESSION"; +export const SET_SETTINGS = "SET_SETTINGS"; export const SET_ENTITY_TYPES = "SET_ENTITY_TYPES"; export const SET_COLLAPSE = "SET_COLLAPSE"; export const LOADING = "LOADING"; diff --git a/pages/admin/settings.js b/pages/admin/settings.js index e26a7605..e390e7b1 100644 --- a/pages/admin/settings.js +++ b/pages/admin/settings.js @@ -1,48 +1,51 @@ import InnerSingleLayout from "@/components/layouts/InnerSingleLayout"; import CacheContext from "@/components/contexts/CacheContext"; +import RootContext from "@/components/contexts/RootContext"; import { getSessionCache } from "@klaudsol/commons/lib/Session"; import { Formik, Form, Field } from "formik"; -import { useRef, useReducer, useEffect, useState, useCallback } from "react"; +import { + useRef, + useReducer, + useContext, + useEffect, + useState, + useCallback, +} from "react"; import AppButtonLg from "@/components/klaudsolcms/buttons/AppButtonLg"; import AppButtonSpinner from "@/components/klaudsolcms/AppButtonSpinner"; +import { sortByOrderAsc } from "@/components/Util"; import { slsFetch } from "@klaudsol/commons/lib/Client"; /** react-icons */ -import { FaCheck } from "react-icons/fa"; +import { FaCheck, FaImage, FaTrash } from "react-icons/fa"; import UploadRenderer from "@/components/renderers/admin/UploadRenderer"; import { settingReducer, initialState, } from "@/components/reducers/settingReducer"; -import { SAVING, CLEANUP, SET_VALUES, SET_ERROR } from "@/lib/actions"; +import { + SAVING, + LOADING, + DELETING, + CLEANUP, + SET_VALUES, + SET_CHANGED, + SET_ERROR, +} from "@/lib/actions"; +import { defaultLogo } from "@/constants/index"; +import { getFilesToDelete, getBody } from "@/lib/s3FormController"; +import { uploadFilesToUrl } from "@/backend/data_access/S3"; +import { validImageTypes } from "@/lib/Constants"; import { readSettings, modifyLogo } from "@/lib/Constants"; export default function Settings({ cache }) { const formRef = useRef(); const [state, dispatch] = useReducer(settingReducer, initialState); + const { state: rootState, dispatch: rootDispatch } = useContext(RootContext); const isValueExists = Object.keys(state.values).length !== 0; const capabilities = cache?.capabilities; - useEffect(() => { - (async () => { - try { - const response = await slsFetch("/api/settings/"); - const { data: dataRaw } = await response.json(); - - const data = dataRaw.reduce((acc, curr) => { - return { ...acc, [curr.setting]: curr.value }; - }, {}); - - dispatch({ type: SET_VALUES, payload: data }); - } catch (error) { - dispatch({ type: SET_ERROR, payload: error.message }); - } finally { - dispatch({ type: CLEANUP }); - } - })(); - }, []); - const onSubmit = (evt) => { evt.preventDefault(); formRef.current.handleSubmit(); @@ -59,7 +62,7 @@ export default function Settings({ cache }) { const formikParams = { innerRef: formRef, - initialValues: state.values, + initialValues: rootState.settings, enableReinitialize: true, onSubmit: (values) => { (async () => { From f50b8ba03f19eb95c7430d4b573db7e60a28c281 Mon Sep 17 00:00:00 2001 From: johnservo Date: Mon, 27 Mar 2023 19:07:42 +0800 Subject: [PATCH 5/8] Populated necessary areas --- backend/models/core/Setting.js | 8 ++++---- components/elements/inner/AppSidebar.js | 11 ++++++++++- components/reducers/contentManagerReducer.js | 8 +++++++- lib/actions.js | 1 + pages/admin.js | 8 ++++++-- .../admin/content-manager/[entity_type_slug]/index.js | 10 +++++++--- pages/admin/settings.js | 2 +- 7 files changed, 36 insertions(+), 12 deletions(-) diff --git a/backend/models/core/Setting.js b/backend/models/core/Setting.js index bb6fe7dc..ddc01ac5 100644 --- a/backend/models/core/Setting.js +++ b/backend/models/core/Setting.js @@ -23,7 +23,7 @@ class Resource { static async getLogo() { const db = new DB(); - const sql = `SELECT * FROM settings WHERE setting = "mainlogo"`; + const sql = `SELECT * FROM settings WHERE setting = "main_logo"`; const data = await db.executeStatement(sql); const output = data.records.map( @@ -89,9 +89,9 @@ class Resource { `; const valueParams = Object.keys(entry).map((e) => [ - { name: 'setting', value: { stringValue: e }}, - { name: 'value', value: { stringValue: entry[e] }} - ]) + { name: "setting", value: { stringValue: e } }, + { name: "value", value: { stringValue: entry[e] } }, + ]); await db.batchExecuteStatement(sql, valueParams); diff --git a/components/elements/inner/AppSidebar.js b/components/elements/inner/AppSidebar.js index 65cad477..015b7d06 100644 --- a/components/elements/inner/AppSidebar.js +++ b/components/elements/inner/AppSidebar.js @@ -98,9 +98,18 @@ const AppSidebar = () => { useEffect(() => { (async () => { await loadEntityTypes({rootState, rootDispatch}); - await loadSettings({ rootState, rootDispatch }); })(); }, [rootState]); + + useEffect(() => { + (async () => { + // We need the settings at rootState because it's used in multiple + // parts of the program. I decided to load the settings in the + // sidebar because this is the only 'common denominator' among all pages. + // We need to have a layout component that covers all pages. + await loadSettings({ rootState, rootDispatch }); + })() + }, []) return ( <> diff --git a/components/reducers/contentManagerReducer.js b/components/reducers/contentManagerReducer.js index ea89e130..26125aa6 100644 --- a/components/reducers/contentManagerReducer.js +++ b/components/reducers/contentManagerReducer.js @@ -11,7 +11,8 @@ import { ROWS_SET, PAGE_SETS_RENDERER, SET_FIRST_FETCH, - TOGGLE_VIEW + TOGGLE_VIEW, + SET_VIEW } from "@/lib/actions"; export const initialState = { @@ -103,5 +104,10 @@ export const contentManagerReducer = (state, action) => { ...state, view: state.view === 'list' ? 'icon' : 'list' }; + case SET_VIEW: + return { + ...state, + view: action.payload + }; } }; diff --git a/lib/actions.js b/lib/actions.js index 8f894558..011a7612 100644 --- a/lib/actions.js +++ b/lib/actions.js @@ -36,4 +36,5 @@ export const SET_FORCE_CHANGE_PASSWORD= "SET_FORCE_CHANGE_PASSWORD"; export const SET_ERROR = 'SET_ERROR'; export const SET_ALL_VALIDATES = 'SET_ALL_VALIDATES'; export const TOGGLE_VIEW = 'TOGGLE_VIEW'; +export const SET_VIEW = 'SET_VIEW'; diff --git a/pages/admin.js b/pages/admin.js index bb029bcc..1c35ff0a 100644 --- a/pages/admin.js +++ b/pages/admin.js @@ -1,5 +1,6 @@ import BasicLayout from "@/components/layouts/BasicLayout"; import CacheContext from "@/components/contexts/CacheContext"; +import RootContext from "@/components/contexts/RootContext"; import { getSessionCache } from "@klaudsol/commons/lib/Session"; import { useState, useEffect, useContext } from "react"; @@ -10,13 +11,16 @@ import { FcVoicePresentation } from "react-icons/fc"; export default function Admin({cache}) { const { firstName = null, lastName = null } = cache ?? {}; + const { state } = useContext(RootContext); + const cmsName = state.settings.cms_name + return (
-

Hello, {firstName}.

Welcome!

+

Hello, {firstName}.

Welcome to {cmsName}!

@@ -31,4 +35,4 @@ export default function Admin({cache}) { ); } -export const getServerSideProps = getSessionCache(); \ No newline at end of file +export const getServerSideProps = getSessionCache(); diff --git a/pages/admin/content-manager/[entity_type_slug]/index.js b/pages/admin/content-manager/[entity_type_slug]/index.js index 24aedd80..2e07daba 100644 --- a/pages/admin/content-manager/[entity_type_slug]/index.js +++ b/pages/admin/content-manager/[entity_type_slug]/index.js @@ -41,7 +41,8 @@ import { SET_FIRST_FETCH, SET_PAGE, PAGE_SETS_RENDERER, - TOGGLE_VIEW + TOGGLE_VIEW, + SET_VIEW } from "@/lib/actions"; import AppContentPagination from "components/klaudsolcms/pagination/AppContentPagination"; import { defaultPageRender, maximumNumberOfPage, EntryValues, writeContents} from "lib/Constants" @@ -52,7 +53,7 @@ export default function ContentManager({ cache }) { const capabilities = cache?.capabilities; const { entity_type_slug } = router.query; const controllerRef = useRef(); - const { state: {currentContentType} } = useContext(RootContext); + const { state: {settings, currentContentType} } = useContext(RootContext); const [state, dispatch] = useReducer(contentManagerReducer, initialState); @@ -102,12 +103,15 @@ export default function ContentManager({ cache }) { })(); }, [entity_type_slug, state.page, state.entry, state.setsRenderer]); - useEffect(() => { dispatch({type: SET_PAGE,payload: defaultPageRender}); dispatch({type: PAGE_SETS_RENDERER,payload: defaultPageRender}); }, [entity_type_slug]); + useEffect(() => { + dispatch({ type: SET_VIEW, payload: settings.default_view }); + }, [state.firstFetch]) + const handleView = () => { dispatch({ type: TOGGLE_VIEW }) } diff --git a/pages/admin/settings.js b/pages/admin/settings.js index e390e7b1..9a222ea7 100644 --- a/pages/admin/settings.js +++ b/pages/admin/settings.js @@ -119,7 +119,7 @@ export default function Settings({ cache }) {
Default view - +
From b4162e25a10d95d7ffd95c53298230dca24990eb Mon Sep 17 00:00:00 2001 From: johnservo Date: Mon, 27 Mar 2023 19:16:55 +0800 Subject: [PATCH 6/8] Fixed image src --- components/fields/FileField.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/components/fields/FileField.js b/components/fields/FileField.js index a9b06084..c8834dda 100644 --- a/components/fields/FileField.js +++ b/components/fields/FileField.js @@ -47,6 +47,12 @@ const FileField = (props) => { document.body.onfocus = checkIfUnfocused; }; + const getImageSrc = () => { + if(value?.name === 'default') return "/logo-180x180.png"; + + return value?.link ?? staticLink ?? defaultImage; + } + return (
@@ -65,7 +71,7 @@ const FileField = (props) => { value={value?.name || ""} onClick={openUploadMenu} > - {value?.name} + {value?.name === 'default' ? 'Default Logo' : value.name} )} {!props.hideUpload && {
{(value || staticLink) && ( {value?.name Date: Mon, 27 Mar 2023 19:37:11 +0800 Subject: [PATCH 7/8] Styled settings page --- pages/admin/settings.js | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/pages/admin/settings.js b/pages/admin/settings.js index 9a222ea7..6e183ed0 100644 --- a/pages/admin/settings.js +++ b/pages/admin/settings.js @@ -116,19 +116,27 @@ export default function Settings({ cache }) {
-
- Default view - - - - -
-
- CMS Name - +
+
+

CMS Name

+ +
+
+

+ Default view +

+ + + + +
- Logo +

Logo

From 90c3dd1f37d9e0c889e32c7a4f0c87f657a49bd7 Mon Sep 17 00:00:00 2001 From: johnservo Date: Mon, 27 Mar 2023 19:51:57 +0800 Subject: [PATCH 8/8] Added margin on settings header --- pages/admin/settings.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pages/admin/settings.js b/pages/admin/settings.js index 6e183ed0..af9dfe13 100644 --- a/pages/admin/settings.js +++ b/pages/admin/settings.js @@ -106,7 +106,7 @@ export default function Settings({ cache }) {
-
+

Settings