diff --git a/README.md b/README.md index 2d250f59..921fe43e 100644 --- a/README.md +++ b/README.md @@ -66,7 +66,7 @@ Screnshots: https://cms.demo.klaudsol.app image - +Testing..... diff --git a/backend/models/core/Entity.js b/backend/models/core/Entity.js index 9353f027..b96a6a56 100644 --- a/backend/models/core/Entity.js +++ b/backend/models/core/Entity.js @@ -258,14 +258,21 @@ class Entity { ? { booleanValue: entry[attributeName] } : { isNull: true }, }, + { + name: "value_datetime", + value: + attributeType == "datetime" + ? { stringValue: entry[attributeName] } + : { isNull: true }, + }, ], ]; }, []); //Insert Values by batch const insertValuesBatchSQL = `INSERT INTO \`values\`(entity_id, attribute_id, - value_string, value_long_string, value_double, value_boolean - ) VALUES (:entity_id, :attribute_id, :value_string, :value_long_string, :value_double, :value_boolean) + value_string, value_long_string, value_datetime, value_double, value_boolean + ) VALUES (:entity_id, :attribute_id, :value_string, :value_long_string, :value_datetime, :value_double, :value_boolean) `; await db.batchExecuteStatement(insertValuesBatchSQL, valueBatchParams); @@ -452,6 +459,13 @@ class Entity { ? { booleanValue: entries[attributeName] } : { isNull: true }, }, + { + name: "value_datetime", + value: + attributeType == "datetime" + ? { stringValue: entries[attributeName] } + : { isNull: true }, + }, ], ]; }, []); @@ -487,8 +501,8 @@ class Entity { if (nonExistingVal.length) { const insertValuesBatchSQL = `INSERT INTO \`values\`(entity_id, attribute_id, - value_string, value_long_string, value_double, value_boolean - ) VALUES (:entity_id, :attribute_id, :value_string, :value_long_string, :value_double, :value_boolean) + value_string, value_long_string, value_datetime, value_double, value_boolean + ) VALUES (:entity_id, :attribute_id, :value_string, :value_long_string, :value_datetime, :value_double, :value_boolean) `; await db.batchExecuteStatement(insertValuesBatchSQL, nonExistingVal); @@ -499,6 +513,7 @@ class Entity { const updateValuesBatchSQL = `UPDATE \`values\` SET value_string = :value_string, value_long_string = :value_long_string, + value_datetime = :value_datetime, value_double = :value_double, value_boolean = :value_boolean WHERE entity_id = :entity_id AND attribute_id = :attribute_id @@ -511,4 +526,4 @@ class Entity { } } -export default Entity; +export default Entity; \ No newline at end of file diff --git a/components/EntityAttributeValue.js b/components/EntityAttributeValue.js index de01eac3..4f42f28b 100644 --- a/components/EntityAttributeValue.js +++ b/components/EntityAttributeValue.js @@ -47,6 +47,8 @@ const formatImage = (key) => { case 'float': //TODO: Find a more accurate representation of float return Number(item.value_double); + case 'datetime': + return item.value_datetime; case 'custom': //TODO: In the future, everything would pass this code @@ -62,4 +64,4 @@ const formatImage = (key) => { return attributeType.toDatabase(item); } - } + } \ No newline at end of file diff --git a/components/attribute_types/AttributeType.js b/components/attribute_types/AttributeType.js index c4e84a33..3ec6ce90 100644 --- a/components/attribute_types/AttributeType.js +++ b/components/attribute_types/AttributeType.js @@ -12,6 +12,7 @@ export default class AttributeType { static TEXT_CMS_TYPE = 'text'; static TEXTAREA_CMS_TYPE = 'textarea'; static RICH_TEXT_CMS_TYPE = 'rich-text'; + static DATETIME_CMS_TYPE = 'datetime'; static FILE_CMS_TYPE = 'file'; static CUSTOM = 'custom'; diff --git a/components/attribute_types/AttributeTypeFactory.js b/components/attribute_types/AttributeTypeFactory.js index 035d3e7d..a6605435 100644 --- a/components/attribute_types/AttributeTypeFactory.js +++ b/components/attribute_types/AttributeTypeFactory.js @@ -4,6 +4,7 @@ import TextareaAttributeType from "@/components/attribute_types/TextareaAttribut import LegacyAttributeType from '@/components/attribute_types/LegacyAttributeType'; import { plugin } from '@/components/plugin/plugin'; import RichTextAttributeType from '@/components/attribute_types/RichTextAttributeType'; +import DateTimeAttributeType from '@/components/attribute_types/DateTimeAttributeType'; import FileAtrributeType from './FileAttributeType'; export default class AttributeTypeFactory { @@ -15,6 +16,8 @@ export default class AttributeTypeFactory { return new TextareaAttributeType({data, metadata}); case AttributeType.RICH_TEXT_CMS_TYPE: return new RichTextAttributeType({data, metadata}); + case AttributeType.DATETIME_CMS_TYPE: + return new DateTimeAttributeType({data, metadata}); case AttributeType.FILE_CMS_TYPE: return new FileAtrributeType({data, metadata}); case AttributeType.CUSTOM: diff --git a/components/attribute_types/DateTimeAttributeType.js b/components/attribute_types/DateTimeAttributeType.js new file mode 100644 index 00000000..371045c8 --- /dev/null +++ b/components/attribute_types/DateTimeAttributeType.js @@ -0,0 +1,51 @@ +import AttributeType from '@/components/attribute_types/AttributeType'; +import { useFormikContext, useField } from "formik"; +import { useState, useEffect } from 'react'; +import DatePicker from 'react-datepicker'; +import 'react-datepicker/dist/react-datepicker.css'; +import moment from 'moment'; + +const DateTimeAttributeReadOnlyComponent = ({ text }) => { + const parsedDate = new Date(text); + const formattedDate = moment(parsedDate).format('MMM. D, YYYY - hh:mm A'); + return <>{formattedDate}; +}; + +const DateTimeAttributeEditableComponent = ({ name }) => { + const { setFieldValue } = useFormikContext(); + const [{ value }] = useField(name); + const [selectedDate, setSelectedDate] = useState(value ? new Date(value) : new Date()); // Set initial selected date to today + + useEffect(() => { + if (selectedDate) { + const formattedDate = moment(selectedDate).format('YYYY-MM-DD HH:mm'); + setFieldValue(name, formattedDate); + } else { + setFieldValue(name, null); + } + }, [selectedDate, name, setFieldValue]); + + const handleDateChange = (date) => { + setSelectedDate(date); + }; + + return ( + + ); +}; + +export default class DateTimeAttributeType extends AttributeType { + readOnlyComponent() { + return DateTimeAttributeReadOnlyComponent; + } + + editableComponent() { + return DateTimeAttributeEditableComponent; + } +} \ No newline at end of file diff --git a/components/cmsTypes.js b/components/cmsTypes.js index 697098db..4163d08c 100644 --- a/components/cmsTypes.js +++ b/components/cmsTypes.js @@ -14,6 +14,7 @@ export const CMS_TYPES = { CHECKBOX: "checkbox", CUSTOM: "custom", RICH_TEXT: "rich-text", + DATETIME: "datetime", FILE: "file" }; @@ -44,4 +45,3 @@ export const resourceValueTypes = [ "value_double", "value_boolean", ]; - diff --git a/components/renderers/admin/AdminRenderer.js b/components/renderers/admin/AdminRenderer.js index d55bc4fd..f15907ad 100644 --- a/components/renderers/admin/AdminRenderer.js +++ b/components/renderers/admin/AdminRenderer.js @@ -11,6 +11,7 @@ import VideoRenderer from "./VideoRenderer"; import BooleanRenderer from "./BooleanRenderer"; import { plugin } from "@/components/plugin/plugin"; import RichTextAttributeType from "@/components/attribute_types/RichTextAttributeType"; +import DateTimeAttributeType from "@/components/attribute_types/DateTimeAttributeType"; import AttributeTypeFactory from "@/components/attribute_types/AttributeTypeFactory"; const AdminRenderer = ({ type, ...params }) => { @@ -35,6 +36,7 @@ const AdminRenderer = ({ type, ...params }) => { case CMS_TYPES.BOOLEAN: return ; case CMS_TYPES.RICH_TEXT: + case CMS_TYPES.DATETIME: case CMS_TYPES.FILE: case CMS_TYPES.CUSTOM: const attributeType = AttributeTypeFactory.create({metadata: {type, custom_name: params.customName, id: params.id}}); @@ -45,4 +47,4 @@ const AdminRenderer = ({ type, ...params }) => { } }; -export default AdminRenderer; +export default AdminRenderer; \ No newline at end of file diff --git a/constants/index.js b/constants/index.js index 8519c65f..9551a053 100644 --- a/constants/index.js +++ b/constants/index.js @@ -7,6 +7,7 @@ export const inputValues = [ { value: "gallery", option: "Gallery" }, { value: "float", option: "Number" }, { value: "video", option: "Video" }, + { value: "datetime", option: "DateTime" }, { value: "boolean", option: "Boolean" }, { value: "file", option: "File" }, { value: "custom", option: "Custom" } @@ -45,4 +46,3 @@ export const operators = { }; export const slugTooltipText = "Slugs are the URL-friendly names of your contents. You can access the contens in your API via their numerical ID (e.g. /api/articles/12) or their slug (e.g. /api/articles/my-first-blog-post)"; - diff --git a/db/migrations/20230427114000_connect_read_pending_users_capabilities.json b/db/migrations/20230427114000_connect_read_pending_users_capabilities.json index 557f2d99..1f427b10 100644 --- a/db/migrations/20230427114000_connect_read_pending_users_capabilities.json +++ b/db/migrations/20230427114000_connect_read_pending_users_capabilities.json @@ -8,4 +8,4 @@ "DELETE FROM group_capabilities WHERE group_id IN (1, 2) ", "AND capabilities_id = (SELECT id FROM capabilities WHERE name = 'read_pending_users' AND is_system_supplied = true);" ] -} +} \ No newline at end of file diff --git a/package.json b/package.json index f5e70ae5..f9e63916 100644 --- a/package.json +++ b/package.json @@ -58,7 +58,7 @@ "react-component-export-image": "^1.0.6", "react-confirm-alert": "2.0.2", "react-csv-downloader": "^2.8.0", - "react-datepicker": "^4.8.0", + "react-datepicker": "^4.15.0", "react-dom": "^18.2.0", "react-hook-form": "^7.33.0", "react-icons": "^4.3.1", diff --git a/styles/klaudsolcms.scss b/styles/klaudsolcms.scss index cd635cc4..cf20565f 100644 --- a/styles/klaudsolcms.scss +++ b/styles/klaudsolcms.scss @@ -1398,3 +1398,21 @@ $onFocus: #323C4E; font-weight: 590; } } + +/** +====================================================== + MODIFY DATE PICKER DESIGN +====================================================== +**/ +.react-datepicker__input-container input { + padding: 8px; + border-radius: 5px; + border: 1px solid #ccc !important; +} +// change the selected date color +.react-datepicker__day--selected, .react-datepicker__time-list-item--selected { + background-color: #467286 !important; +} +.react-datepicker__time-container { + overflow: hidden; +} diff --git a/yarn.lock b/yarn.lock index 4d0d8c4c..d88bf603 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4294,10 +4294,10 @@ react-csv-downloader@^2.8.0: dependencies: file-saver "^2.0.2" -react-datepicker@^4.8.0: - version "4.11.0" - resolved "https://registry.yarnpkg.com/react-datepicker/-/react-datepicker-4.11.0.tgz#40e73b4729a284ed206fdb322b8e84eb566e11a3" - integrity sha512-50n93o7mQwBEhg05tbopjFKgs8qgi8VBCAOMC4VqrKut72eAjESc/wXS/k5hRtnP0oe2FCGw7MJuIwh37wuXOw== +react-datepicker@^4.15.0: + version "4.15.0" + resolved "https://registry.yarnpkg.com/react-datepicker/-/react-datepicker-4.15.0.tgz#489834773fbcf87852273b4642f0c5bd3811cef7" + integrity sha512-kysEqVv6wRQkmAyn0wJi4Xx+JjBPBtXWfQSfh6sR3wdzZX1/LjYTPmaurnVI6ao177ecompg8ze7NCgtEGW78A== dependencies: "@popperjs/core" "^2.9.2" classnames "^2.2.6"