diff --git a/_doc/ToDo.md b/_doc/ToDo.md index fa13d7e..1bc87ef 100644 --- a/_doc/ToDo.md +++ b/_doc/ToDo.md @@ -259,7 +259,7 @@ in schedule admin - if a publisher is always pair & family is not in the shift - [] fix transport UI [] revise import/export to word [] allow keyman/scheduler role -[] allow blocking of inputs (different from publishing) +[] allow blocking of inputs (different from publishing) TODO: fix to keep previous occurances when repeating evert week [] user - add createdAt field [] FIX insecure logins \ No newline at end of file diff --git a/components/availability/AvailabilityList.js b/components/availability/AvailabilityList.js index 6447c00..0add9e9 100644 --- a/components/availability/AvailabilityList.js +++ b/components/availability/AvailabilityList.js @@ -17,11 +17,22 @@ export default function AvailabilityList({ publisher, showNew }) { const [showAv, setShowAv] = useState(showNew || false); const [selectedItem, setSelectedItem] = useState(null); const [items, setItems] = useState(publisher.availabilities); // Convert items prop to state + const [blockedAvailabilityDate, setBlockedAvailabilityDate] = useState(null); useEffect(() => { console.log('items set to:', items?.map(item => item.id)); }, [items]) + useEffect(() => { + axiosInstance.get(`/api/?action=settings&key=AvailabilityBlockDate`) + .then(({ data }) => { + setBlockedAvailabilityDate(new Date(data.value)); + }) + .catch(error => { + console.error("Error getting blocked date:", error); + }); + }, []); + const toggleAv = () => setShowAv(!showAv); const editAvailability = (item) => { setSelectedItem(item); @@ -72,9 +83,16 @@ export default function AvailabilityList({ publisher, showNew }) { {/* */} - + {(blockedAvailabilityDate && new Date(item.startTime) < blockedAvailabilityDate) ? ( + + ) : ( + + )} + ))} diff --git a/package-lock.json b/package-lock.json index 97b71d3..29825c8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { - "name": "pwwa", - "version": "1.2.2", + "name": "smws", + "version": "1.2.4", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "pwwa", - "version": "1.2.2", + "name": "smws", + "version": "1.2.4", "dependencies": { "@auth/prisma-adapter": "^1.4.0", "@emotion/react": "^11.11.3", @@ -16,7 +16,7 @@ "@mui/material": "^5.15.10", "@mui/x-date-pickers": "^6.19.4", "@premieroctet/next-crud": "^3.0.0", - "@prisma/client": "^5.13.0", + "@prisma/client": "^5.15.0", "@react-pdf/renderer": "^3.3.8", "@tailwindcss/forms": "^0.5.7", "@types/multer": "^1.4.11", @@ -99,7 +99,7 @@ "devDependencies": { "cross-env": "^7.0.3", "depcheck": "^1.4.7", - "prisma": "^5.13.0" + "prisma": "^5.15.0" } }, "node_modules/@alloc/quick-lru": { @@ -3914,9 +3914,9 @@ } }, "node_modules/@prisma/client": { - "version": "5.13.0", - "resolved": "https://registry.npmjs.org/@prisma/client/-/client-5.13.0.tgz", - "integrity": "sha512-uYdfpPncbZ/syJyiYBwGZS8Gt1PTNoErNYMuqHDa2r30rNSFtgTA/LXsSk55R7pdRTMi5pHkeP9B14K6nHmwkg==", + "version": "5.15.0", + "resolved": "https://registry.npmjs.org/@prisma/client/-/client-5.15.0.tgz", + "integrity": "sha512-wPTeTjbd2Q0abOeffN7zCDCbkp9C9cF+e9HPiI64lmpehyq2TepgXE+sY7FXr7Rhbb21prLMnhXX27/E11V09w==", "hasInstallScript": true, "engines": { "node": ">=16.13" @@ -3931,39 +3931,39 @@ } }, "node_modules/@prisma/debug": { - "version": "5.13.0", - "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-5.13.0.tgz", - "integrity": "sha512-699iqlEvzyCj9ETrXhs8o8wQc/eVW+FigSsHpiskSFydhjVuwTJEfj/nIYqTaWFYuxiWQRfm3r01meuW97SZaQ==", + "version": "5.15.0", + "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-5.15.0.tgz", + "integrity": "sha512-QpEAOjieLPc/4sMny/WrWqtpIAmBYsgqwWlWwIctqZO0AbhQ9QcT6x2Ut3ojbDo/pFRCCA1Z1+xm2MUy7fAkZA==", "devOptional": true }, "node_modules/@prisma/engines": { - "version": "5.13.0", - "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-5.13.0.tgz", - "integrity": "sha512-hIFLm4H1boj6CBZx55P4xKby9jgDTeDG0Jj3iXtwaaHmlD5JmiDkZhh8+DYWkTGchu+rRF36AVROLnk0oaqhHw==", + "version": "5.15.0", + "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-5.15.0.tgz", + "integrity": "sha512-hXL5Sn9hh/ZpRKWiyPA5GbvF3laqBHKt6Vo70hYqqOhh5e0ZXDzHcdmxNvOefEFeqxra2DMz2hNbFoPvqrVe1w==", "devOptional": true, "hasInstallScript": true, "dependencies": { - "@prisma/debug": "5.13.0", - "@prisma/engines-version": "5.13.0-23.b9a39a7ee606c28e3455d0fd60e78c3ba82b1a2b", - "@prisma/fetch-engine": "5.13.0", - "@prisma/get-platform": "5.13.0" + "@prisma/debug": "5.15.0", + "@prisma/engines-version": "5.15.0-29.12e25d8d06f6ea5a0252864dd9a03b1bb51f3022", + "@prisma/fetch-engine": "5.15.0", + "@prisma/get-platform": "5.15.0" } }, "node_modules/@prisma/engines-version": { - "version": "5.13.0-23.b9a39a7ee606c28e3455d0fd60e78c3ba82b1a2b", - "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-5.13.0-23.b9a39a7ee606c28e3455d0fd60e78c3ba82b1a2b.tgz", - "integrity": "sha512-AyUuhahTINGn8auyqYdmxsN+qn0mw3eg+uhkp8zwknXYIqoT3bChG4RqNY/nfDkPvzWAPBa9mrDyBeOnWSgO6A==", + "version": "5.15.0-29.12e25d8d06f6ea5a0252864dd9a03b1bb51f3022", + "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-5.15.0-29.12e25d8d06f6ea5a0252864dd9a03b1bb51f3022.tgz", + "integrity": "sha512-3BEgZ41Qb4oWHz9kZNofToRvNeS4LZYaT9pienR1gWkjhky6t6K1NyeWNBkqSj2llgraUNbgMOCQPY4f7Qp5wA==", "devOptional": true }, "node_modules/@prisma/fetch-engine": { - "version": "5.13.0", - "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-5.13.0.tgz", - "integrity": "sha512-Yh4W+t6YKyqgcSEB3odBXt7QyVSm0OQlBSldQF2SNXtmOgMX8D7PF/fvH6E6qBCpjB/yeJLy/FfwfFijoHI6sA==", + "version": "5.15.0", + "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-5.15.0.tgz", + "integrity": "sha512-z6AY5yyXxc20Klj7wwnfGP0iIUkVKzybqapT02zLYR/nf9ynaeN8bq73WRmi1TkLYn+DJ5Qy+JGu7hBf1pE78A==", "devOptional": true, "dependencies": { - "@prisma/debug": "5.13.0", - "@prisma/engines-version": "5.13.0-23.b9a39a7ee606c28e3455d0fd60e78c3ba82b1a2b", - "@prisma/get-platform": "5.13.0" + "@prisma/debug": "5.15.0", + "@prisma/engines-version": "5.15.0-29.12e25d8d06f6ea5a0252864dd9a03b1bb51f3022", + "@prisma/get-platform": "5.15.0" } }, "node_modules/@prisma/generator-helper": { @@ -3980,12 +3980,12 @@ "integrity": "sha512-tZ+MOjWlVvz1kOEhNYMa4QUGURY+kgOUBqLHYIV8jmCsMuvA1tWcn7qtIMLzYWCbDcQT4ZS8xDgK0R2gl6/0wA==" }, "node_modules/@prisma/get-platform": { - "version": "5.13.0", - "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-5.13.0.tgz", - "integrity": "sha512-B/WrQwYTzwr7qCLifQzYOmQhZcFmIFhR81xC45gweInSUn2hTEbfKUPd2keAog+y5WI5xLAFNJ3wkXplvSVkSw==", + "version": "5.15.0", + "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-5.15.0.tgz", + "integrity": "sha512-1GULDkW4+/VQb73vihxCBSc4Chc2x88MA+O40tcZFjmBzG4/fF44PaXFxUqKSFltxU9L9GIMLhh0Gfkk/pUbtg==", "devOptional": true, "dependencies": { - "@prisma/debug": "5.13.0" + "@prisma/debug": "5.15.0" } }, "node_modules/@prisma/internals": { @@ -15118,13 +15118,13 @@ "integrity": "sha512-WuxUnVtlWL1OfZFQFuqvnvs6MiAGk9UNsBostyBOB0Is9wb5uRESevA6rnl/rkksXaGX3GzZhPup5d6Vp1nFew==" }, "node_modules/prisma": { - "version": "5.13.0", - "resolved": "https://registry.npmjs.org/prisma/-/prisma-5.13.0.tgz", - "integrity": "sha512-kGtcJaElNRAdAGsCNykFSZ7dBKpL14Cbs+VaQ8cECxQlRPDjBlMHNFYeYt0SKovAVy2Y65JXQwB3A5+zIQwnTg==", + "version": "5.15.0", + "resolved": "https://registry.npmjs.org/prisma/-/prisma-5.15.0.tgz", + "integrity": "sha512-JA81ACQSCi3a7NUOgonOIkdx8PAVkO+HbUOxmd00Yb8DgIIEpr2V9+Qe/j6MLxIgWtE/OtVQ54rVjfYRbZsCfw==", "devOptional": true, "hasInstallScript": true, "dependencies": { - "@prisma/engines": "5.13.0" + "@prisma/engines": "5.15.0" }, "bin": { "prisma": "build/index.js" diff --git a/package.json b/package.json index 7cecc91..64dcddb 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,7 @@ "@mui/material": "^5.15.10", "@mui/x-date-pickers": "^6.19.4", "@premieroctet/next-crud": "^3.0.0", - "@prisma/client": "^5.13.0", + "@prisma/client": "^5.15.0", "@react-pdf/renderer": "^3.3.8", "@tailwindcss/forms": "^0.5.7", "@types/multer": "^1.4.11", @@ -117,6 +117,6 @@ "devDependencies": { "cross-env": "^7.0.3", "depcheck": "^1.4.7", - "prisma": "^5.13.0" + "prisma": "^5.15.0" } -} \ No newline at end of file +} diff --git a/pages/api/index.ts b/pages/api/index.ts index 1a3b699..f26e8cc 100644 --- a/pages/api/index.ts +++ b/pages/api/index.ts @@ -64,6 +64,50 @@ export default async function handler(req, res) { res.status(200).json({ message: "SQL script executed successfully" }); break; + case "settings": + try { + const key = req.query.key; + switch (req.method) { + case "LIST": + let s1 = await prisma.settings.findMany(); + res.status(200).json(s1.map(setting => setting.key)); + break; + case "GET": + const s2 = await prisma.settings.findUnique({ + where: { + key: key + } + }); + res.status(200).json(s2); + + break; + case "PUT": //create or update + const value = req.query.value; + const results = await prisma.settings.upsert({ + where: { + key: key + }, + update: { + value: value + }, + create: { + key: key, + value: value + } + }); + res.status(200).json(results); + break; + default: + res.status(405).json({ message: "Method Not Allowed" }); + break; + } + } catch (error) { + console.error("Error getting settings: " + error); + res.status(500).json({ error }); + } + break; + + case "deleteAllPublishers": //get filter and delete all publishers containing that in first name or last name await prisma.publisher.deleteMany({ diff --git a/pages/cart/calendar/index.tsx b/pages/cart/calendar/index.tsx index a94d001..c0e2baf 100644 --- a/pages/cart/calendar/index.tsx +++ b/pages/cart/calendar/index.tsx @@ -62,6 +62,7 @@ export default function CalendarPage({ initialEvents, initialShifts }) { const [allShifts, setAllShifts] = useState(initialShifts); const [isPublished, setIsPublished] = useState(() => initialShifts.some(shift => shift.isPublished)); const [value, onChange] = useState(new Date()); + const [selectedMonth, setSelectedMonth] = useState(new Date().getMonth()); const [shifts, setShifts] = React.useState([]); const [error, setError] = React.useState(null); const [availablePubs, setAvailablePubs] = React.useState([]); @@ -89,7 +90,6 @@ export default function CalendarPage({ initialEvents, initialShifts }) { handleCalDateChange(value); // Call handleCalDateChange whenever isCheckboxChecked changes }, [filterShowWithoutAssignments]); // Dependency array - const [selectedMonth, setSelectedMonth] = useState(new Date().getMonth()); useEffect(() => { const newMonth = value.getMonth(); if (newMonth !== selectedMonth) { @@ -607,6 +607,29 @@ export default function CalendarPage({ initialEvents, initialShifts }) { }); } + /* + + model Settings { + id Int @id @default(autoincrement()) + key String + value String + description String? +} +*/ + async function setAvailabilityBlockDate(AvailabilityBlockDate: Date): Promise { + // set AvailabilityBlockDate to the selected date + let monthInfo = common.getMonthInfo(value); + await axiosInstance.put(`/api/?action=settings&key=AvailabilityBlockDate&value=${common.getISODateOnly(monthInfo.lastSunday)}`) + .then((response) => { + console.log("AvailabilityBlockDate set to:", response.data); + // setShifts([...shifts, response.data]); + handleCalDateChange(value); + } + ).catch((error) => { + console.error("Error setting AvailabilityBlockDate:", error); + }); + } + return ( <> @@ -715,8 +738,9 @@ export default function CalendarPage({ initialEvents, initialShifts }) { - - + {/* + */} + )} @@ -795,7 +819,7 @@ export default function CalendarPage({ initialEvents, initialShifts }) { {pub.canTransport && ()}
- + {pub.currentMonthAvailabilityDaysCount || 0} | {pub.currentMonthAvailabilityHoursCount || 0} {pub.currentWeekAssignments || 0} diff --git a/pages/cart/publishers/stats.tsx b/pages/cart/publishers/stats.tsx index 69e33f6..0e10c77 100644 --- a/pages/cart/publishers/stats.tsx +++ b/pages/cart/publishers/stats.tsx @@ -253,7 +253,7 @@ function ContactsPage({ allPublishers }) { <> {pub.availabilities.length > 0 ? ( - + {pub.currentMonthAvailabilityDaysCount} | {pub.currentMonthAvailabilityHoursCount} ) : 0} diff --git a/pages/dash.tsx b/pages/dash.tsx index 53c74bd..4e2fbd3 100644 --- a/pages/dash.tsx +++ b/pages/dash.tsx @@ -270,7 +270,7 @@ export const getServerSideProps = async (context) => { } }); cartEvents = common.convertDatesToISOStrings(cartEvents); - const lastPublishedDate = (await prisma.shift.findFirst({ + let lastPublishedDate = (await prisma.shift.findFirst({ where: { isPublished: true, }, @@ -280,7 +280,17 @@ export const getServerSideProps = async (context) => { orderBy: { endTime: 'desc' } - })).endTime; + }))?.endTime || new Date(); + + let blockedDate = await prisma.settings.findUnique({ + where: { + key: "AvailabilityBlockDate" + } + }); + if (blockedDate) + blockedDate.value = new Date(blockedDate.value); + + lastPublishedDate = lastPublishedDate > blockedDate.value ? lastPublishedDate : blockedDate.value; return { props: { diff --git a/prisma/migrations/20240614185207_/migration.sql b/prisma/migrations/20240614185207_/migration.sql new file mode 100644 index 0000000..fb8827e --- /dev/null +++ b/prisma/migrations/20240614185207_/migration.sql @@ -0,0 +1,9 @@ +-- CreateTable +CREATE TABLE `Settings` ( + `id` INTEGER NOT NULL AUTO_INCREMENT, + `key` VARCHAR(191) NOT NULL, + `value` VARCHAR(191) NOT NULL, + `description` VARCHAR(191) NULL, + + PRIMARY KEY (`id`) +) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; diff --git a/prisma/migrations/20240614213436_modify_settings_table/migration.sql b/prisma/migrations/20240614213436_modify_settings_table/migration.sql new file mode 100644 index 0000000..8c71f7c --- /dev/null +++ b/prisma/migrations/20240614213436_modify_settings_table/migration.sql @@ -0,0 +1,10 @@ +/* +Warnings: +- The primary key for the `settings` table will be changed. If it partially fails, the table could be left without primary key constraint. +- You are about to drop the column `id` on the `settings` table. All the data in the column will be lost. +*/ +-- AlterTable +ALTER TABLE `Settings` +DROP PRIMARY KEY, +DROP COLUMN `id`, +ADD PRIMARY KEY (`key`); \ No newline at end of file diff --git a/prisma/migrations/20240616203120_add_survey_table/migration.sql b/prisma/migrations/20240616203120_add_survey_table/migration.sql new file mode 100644 index 0000000..a53e34c --- /dev/null +++ b/prisma/migrations/20240616203120_add_survey_table/migration.sql @@ -0,0 +1,19 @@ +-- AlterTable +ALTER TABLE `message` ADD COLUMN `answer` VARCHAR(191) NULL, + ADD COLUMN `answerDate` DATETIME(3) NULL, + ADD COLUMN `shownDate` DATETIME(3) NULL, + ADD COLUMN `surveyId` INTEGER NULL; + +-- CreateTable +CREATE TABLE `Survey` ( + `id` INTEGER NOT NULL AUTO_INCREMENT, + `content` VARCHAR(191) NOT NULL, + `answers` JSON NULL, + `publicFrom` DATETIME(3) NULL, + `publicUntil` DATETIME(3) NULL, + + PRIMARY KEY (`id`) +) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + +-- AddForeignKey +ALTER TABLE `Message` ADD CONSTRAINT `Message_surveyId_fkey` FOREIGN KEY (`surveyId`) REFERENCES `Survey`(`id`) ON DELETE SET NULL ON UPDATE CASCADE; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index c5cc4d8..0ecb8b8 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -264,6 +264,15 @@ enum MessageType { InApp } +model Survey { + id Int @id @default(autoincrement()) + content String + answers Json? + messages Message[] + publicFrom DateTime? + publicUntil DateTime? +} + model Message { id Int @id @default(autoincrement()) publisher Publisher @relation(fields: [publisherId], references: [id]) @@ -274,6 +283,12 @@ model Message { isPublic Boolean @default(false) type MessageType @default(Email) publicUntil DateTime? + shownDate DateTime? + answer String? + answerDate DateTime? + + Survey Survey? @relation(fields: [surveyId], references: [id]) + surveyId Int? } enum EventLogType { @@ -348,3 +363,9 @@ model VerificationToken { @@unique([identifier, token]) } + +model Settings { + key String @id + value String + description String? +}