Merge branch 'production'

This commit is contained in:
Dobromir Popov
2024-04-18 12:35:01 +03:00
8 changed files with 99 additions and 25 deletions

1
.env
View File

@ -8,6 +8,7 @@ NEXTAUTH_SECRET=ed8a9681efc414df89dfd03cd188ed58
NODE_ENV=development NODE_ENV=development
# mysql # mysql
DATABASE=mysql://cart:cartpw@localhost:3306/cart
# // owner: dobromir.popov@gmail.com | Специално Свидетелстване София # // owner: dobromir.popov@gmail.com | Специално Свидетелстване София
# // https://console.cloud.google.com/apis/credentials/oauthclient/926212607479-d3m8hm8f8esp3rf1639prskn445sa01v.apps.googleusercontent.com?project=grand-forge-108716 # // https://console.cloud.google.com/apis/credentials/oauthclient/926212607479-d3m8hm8f8esp3rf1639prskn445sa01v.apps.googleusercontent.com?project=grand-forge-108716

View File

@ -4,7 +4,7 @@ import { toast } from 'react-toastify';
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import Link from "next/link"; import Link from "next/link";
import DayOfWeek from "../DayOfWeek"; import DayOfWeek from "../DayOfWeek";
import { Location, UserRole } from "@prisma/client"; import { ReportType } from "@prisma/client";
const common = require('src/helpers/common'); const common = require('src/helpers/common');
import { useSession } from "next-auth/react" import { useSession } from "next-auth/react"
@ -97,6 +97,7 @@ export default function ExperienceForm({ publisherId, assgnmentId, existingItem,
e.preventDefault(); e.preventDefault();
item.publisher = { connect: { id: pubId } }; item.publisher = { connect: { id: pubId } };
item.location = { connect: { id: parseInt(item.locationId) } }; item.location = { connect: { id: parseInt(item.locationId) } };
item.type = ReportType.Experience;
delete item.locationId; delete item.locationId;
try { try {

View File

@ -4,7 +4,7 @@ import { toast } from 'react-toastify';
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import Link from "next/link"; import Link from "next/link";
import DayOfWeek from "../DayOfWeek"; import DayOfWeek from "../DayOfWeek";
import { Location, UserRole } from "@prisma/client"; import { ReportType } from "@prisma/client";
const common = require('src/helpers/common'); const common = require('src/helpers/common');
import { useSession } from "next-auth/react" import { useSession } from "next-auth/react"
@ -55,7 +55,7 @@ export default function FeedbackForm({ publisherId, onDone }) {
assignmentId: 0, assignmentId: 0,
publisherId: publisherId, publisherId: publisherId,
date: new Date(), date: new Date(),
placementCount: 0, placementCount: 1,
videoCount: 0, videoCount: 0,
returnVisitInfoCount: 0, returnVisitInfoCount: 0,
conversationCount: 0 conversationCount: 0
@ -73,6 +73,8 @@ export default function FeedbackForm({ publisherId, onDone }) {
const handleSubmit = async (e) => { const handleSubmit = async (e) => {
e.preventDefault(); e.preventDefault();
item.publisher = { connect: { id: pubId } }; item.publisher = { connect: { id: pubId } };
//ToDo: create dedicated feedback type instead of using placementCount for subtype
item.type = item.placementCount === 1 ? ReportType.Feedback_Problem : (item.placementCount === 2 ? ReportType.Feedback_Suggestion : ReportType.Feedback);
delete item.assignmentId; delete item.assignmentId;
try { try {

View File

@ -4,6 +4,7 @@ import { toast } from "react-hot-toast";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import Link from "next/link"; import Link from "next/link";
import { useSession } from "next-auth/react" import { useSession } from "next-auth/react"
import { ReportType } from "@prisma/client";
const common = require('src/helpers/common'); const common = require('src/helpers/common');
@ -89,6 +90,7 @@ export default function ReportForm({ shiftId, existingItem, onDone }) {
item.publisher = { connect: { id: publisherId } }; item.publisher = { connect: { id: publisherId } };
item.shift = { connect: { id: parseInt(item.shiftId) } }; item.shift = { connect: { id: parseInt(item.shiftId) } };
item.date = new Date(item.date); item.date = new Date(item.date);
item.type = ReportType.Report;
delete item.publisherId; delete item.publisherId;
delete item.shiftId; delete item.shiftId;
item.placementCount = parseInt(item.placementCount); item.placementCount = parseInt(item.placementCount);

View File

@ -42,10 +42,10 @@ export const authOptions: NextAuthOptions = {
} }
} }
}), }),
AppleProvider({ // AppleProvider({
clientId: process.env.APPLE_APP_ID, // clientId: process.env.APPLE_APP_ID,
clientSecret: process.env.APPLE_SECRET // clientSecret: process.env.APPLE_SECRET
}), // }),
// AzureADProvider({ // AzureADProvider({
// clientId: process.env.AZURE_AD_CLIENT_ID, // clientId: process.env.AZURE_AD_CLIENT_ID,
// clientSecret: process.env.AZURE_AD_CLIENT_SECRET, // clientSecret: process.env.AZURE_AD_CLIENT_SECRET,

View File

@ -9,16 +9,35 @@ import { useSession } from "next-auth/react"
import common from '../../../src/helpers/common'; import common from '../../../src/helpers/common';
import Layout from "../../../components/layout"; import Layout from "../../../components/layout";
import ProtectedRoute from '../../../components/protectedRoute'; import ProtectedRoute from '../../../components/protectedRoute';
import { Location, UserRole } from "@prisma/client"; import { Location, UserRole, ReportType } from "@prisma/client";
export default function Reports() { export default function Reports() {
const [reports, setReports] = useState([]); const [reports, setReports] = useState([]);
const [filteredReports, setFilteredReports] = useState([]);
const router = useRouter(); const router = useRouter();
const { data: session } = useSession(); const { data: session } = useSession();
const [filterType, setFilterType] = useState('ServiceReport');
const handleFilterChange = (event) => {
setFilterType(event.target.value);
console.log("event filter set to:" + event.target.value);
};
useEffect(() => {
const isFeedbackType = type => [
'Feedback_Problem',
'Feedback_Suggestion',
'Feedback'
].includes(type);
setFilteredReports(reports.filter(report =>
filterType === 'Feedback' ? isFeedbackType(report.type) : report.type === filterType
));
}, [reports, filterType]);
const deleteReport = (id) => { const deleteReport = (id) => {
axiosInstance axiosInstance
@ -66,7 +85,7 @@ export default function Reports() {
<Layout> <Layout>
<ProtectedRoute allowedRoles={[UserRole.POWERUSER, UserRole.ADMIN, UserRole.USER, UserRole.EXTERNAL]}> <ProtectedRoute allowedRoles={[UserRole.POWERUSER, UserRole.ADMIN, UserRole.USER, UserRole.EXTERNAL]}>
<div className="h-5/6 grid place-items-center"> <div className="h-5/6 grid place-items-start px-4 pt-8">
<div className="flex flex-col w-full px-4"> <div className="flex flex-col w-full px-4">
<h1 className="text-2xl font-bold text-center">Отчети</h1> <h1 className="text-2xl font-bold text-center">Отчети</h1>
<Link href="/cart/reports/report"> <Link href="/cart/reports/report">
@ -74,18 +93,21 @@ export default function Reports() {
Добави нов отчет Добави нов отчет
</button> </button>
</Link> </Link>
<label className="mr-4"> <div className="flex gap-2 mb-4">
<input type="radio" name="reportType" value="ServiceReport" defaultChecked />
Отчети <label className={`cursor-pointer px-4 py-2 rounded-full ${filterType === 'ServiceReport' ? 'bg-blue-500 text-white' : 'bg-gray-200 text-gray-800'}`}>
</label> <input type="radio" name="reportType" value="ServiceReport" checked={filterType === 'ServiceReport'} onChange={handleFilterChange} className="sr-only" />
<label className="mr-4"> Отчети
<input type="radio" name="reportType" value="Experience" /> </label>
Случка <label className={`cursor-pointer px-4 py-2 rounded-full ${filterType === 'Experience' ? 'bg-blue-500 text-white' : 'bg-gray-200 text-gray-800'}`}>
</label> <input type="radio" name="reportType" value="Experience" checked={filterType === 'Experience'} onChange={handleFilterChange} className="sr-only" />
<label className="mr-4"> Случка
<input type="radio" name="reportType" value="Feedback" /> </label>
Отзиви <label className={`cursor-pointer px-4 py-2 rounded-full ${filterType === 'Feedback' ? 'bg-blue-500 text-white' : 'bg-gray-200 text-gray-800'}`}>
</label> <input type="radio" name="reportType" value="Feedback" checked={filterType === 'Feedback'} onChange={handleFilterChange} className="sr-only" />
Отзиви
</label>
</div>
<div className="mt-4 w-full overflow-x-auto"> <div className="mt-4 w-full overflow-x-auto">
<table className="w-full table-auto"> <table className="w-full table-auto">
<thead> <thead>
@ -98,13 +120,13 @@ export default function Reports() {
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{reports.map((report) => ( {filteredReports.map((report) => (
<tr key={report.id}> <tr key={report.id}>
<td className="border px-2 py-2">{report.publisher.firstName + " " + report.publisher.lastName}</td> <td className="border px-2 py-2">{report.publisher.firstName + " " + report.publisher.lastName}</td>
<td className="border px-2 py-2">{common.getDateFormated(new Date(report.date))}</td> <td className="border px-2 py-2">{common.getDateFormated(new Date(report.date))}</td>
<td className="border px-2 py-2">{report.location?.name}</td> <td className="border px-2 py-2">{report.location?.name}</td>
<td className="border px-2 py-2"> <td className="border px-2 py-2">
{(report.experienceInfo === null || report.experienceInfo === "") {(report.type === ReportType.ServiceReport)
? ( ? (
<> <>
<div><strong>Отчет</strong></div> <div><strong>Отчет</strong></div>
@ -113,9 +135,16 @@ export default function Reports() {
Клипове: {report.videoCount} <br /> Клипове: {report.videoCount} <br />
Адреси / Телефони: {report.returnVisitInfoCount} <br /> Адреси / Телефони: {report.returnVisitInfoCount} <br />
</> </>
) : (report.placementCount > 0) ? ( ) : (report.type === ReportType.Feedback || report.type === ReportType.Feedback_Problem || report.type === ReportType.Feedback_Suggestion) ? (
<> <>
<div><strong>Отзив</strong></div> <div>
<strong style={{ fontWeight: 'bold' }}>Отзив</strong>
{report.type === "Feedback_Problem" ?
<span style={{ color: 'red', fontWeight: 'bold' }}> - Проблем</span> :
report.type === "Feedback_Suggestion" ?
<span style={{ color: 'blue' }}> - Предложение</span> :
""}
</div>
<div dangerouslySetInnerHTML={{ __html: report.experienceInfo }} /> <div dangerouslySetInnerHTML={{ __html: report.experienceInfo }} />
</> </>
) : ( ) : (

View File

@ -0,0 +1,20 @@
-- CreateTable
CREATE TABLE `EventLog` (
`id` INTEGER NOT NULL AUTO_INCREMENT,
`date` DATETIME(3) NOT NULL,
`publisherId` VARCHAR(191) NULL,
`shiftId` INTEGER NULL,
`content` VARCHAR(191) NOT NULL,
`type` ENUM('AssignnementReplacementRequested', 'AssignnementReplacement', 'SentEmail') NOT NULL,
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- AddForeignKey
ALTER TABLE `EventLog`
ADD CONSTRAINT `EventLog_publisherId_fkey` FOREIGN KEY (`publisherId`) REFERENCES `Publisher` (`id`) ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE `EventLog`
ADD CONSTRAINT `EventLog_shiftId_fkey` FOREIGN KEY (`shiftId`) REFERENCES `Shift` (`id`) ON DELETE SET NULL ON UPDATE CASCADE;

View File

@ -121,6 +121,7 @@ model Publisher {
comments String? comments String?
reports Report[] reports Report[]
Message Message[] Message Message[]
EventLog EventLog[]
} }
model Availability { model Availability {
@ -179,6 +180,7 @@ model Shift {
reportId Int? @unique reportId Int? @unique
Report Report? @relation(fields: [reportId], references: [id]) Report Report? @relation(fields: [reportId], references: [id])
isPublished Boolean @default(false) //NEW v1.0.1 isPublished Boolean @default(false) //NEW v1.0.1
EventLog EventLog[]
@@map("Shift") @@map("Shift")
} }
@ -256,6 +258,23 @@ model Message {
type MessageType @default(Email) type MessageType @default(Email)
} }
enum EventLogType {
AssignnementReplacementRequested
AssignnementReplacement
SentEmail
}
model EventLog {
id Int @id @default(autoincrement())
date DateTime
publisherId String?
publisher Publisher? @relation(fields: [publisherId], references: [id])
shiftId Int?
shift Shift? @relation(fields: [shiftId], references: [id])
content String
type EventLogType
}
//user auth and session management //user auth and session management
model User { model User {
id String @id @default(cuid()) id String @id @default(cuid())