warehouse locations

This commit is contained in:
Dobromir Popov
2024-12-01 22:26:41 +02:00
parent e67fb399cd
commit c06b93f6ad
13 changed files with 129 additions and 25 deletions

View File

@ -9,6 +9,7 @@ import FileUploadWithPreview from 'components/FileUploadWithPreview ';
import ProtectedRoute, { serverSideAuth } from "../../components/protectedRoute";
import { UserRole } from "@prisma/client";
import { Input } from '@mui/base';
const common = require('src/helpers/common');
@ -67,6 +68,7 @@ export default function LocationForm() {
address: "",
isActive: true,
});
const [isRawHtml, setIsRawHtml] = useState(false);
// const [isEdit, setIsEdit] = useState(false);
@ -178,6 +180,13 @@ export default function LocationForm() {
<label className="label" htmlFor="isActive">Активна</label>
</div>
</div>
{/* is on main menu */}
<div className="mb-4">
<div className="form-check">
<input className="checkbox form-input" type="checkbox" id="isOnMainMenu" name="isOnMainMenu" onChange={handleChange} checked={location.isOnMainMenu} autoComplete="off" />
<label className="label" htmlFor="isOnMainMenu">Покажи в главното меню</label>
</div>
</div>
{/* backupLocation */}
<div className="mb-4">
<label className="label" htmlFor="backupLocation">При дъжд и лошо време</label>
@ -238,12 +247,50 @@ export default function LocationForm() {
<label className="label" htmlFor="content">Content</label>
<TextEditor
ref={quillRef}
value={content}
onChange={setContent}
placeholder="Описание на локацията. Снимки"
prefix={`location-${router.query.id}`} />
<ProtectedRoute allowedRoles={[UserRole.ADMIN]} deniedMessage=" ">
<label className="label" htmlFor="backupLocation">Admin Edit Location HTML</label>
<div className="field mb-4">
<label className="switch">
<input
type="checkbox"
checked={isRawHtml}
onChange={(e) => {
setIsRawHtml(e.target.checked);
}}
/>
<span className="slider round"></span>
<span className="ml-2">Edit Raw HTML</span>
</label>
{isRawHtml && <>
<label className="label" htmlFor="backupLocation">Admin Edit Location HTML</label>
<textarea
className="w-full min-h-[200px] p-3 border rounded-lg shadow-sm focus:ring-2 focus:ring-blue-500 focus:border-blue-500 bg-white dark:bg-gray-800 dark:border-gray-700 dark:text-gray-200 font-mono resize-y"
placeholder="HTML Content"
id="contentHTML_Raw"
name="backupLocation"
// onChange={handleChange}
value={content}
autoComplete="off"
spellCheck="false"
/>
</>
}
</div>
</ProtectedRoute>
{!isRawHtml && <>
<TextEditor
ref={quillRef}
value={content}
onChange={setContent}
placeholder="Описание на локацията. Снимки"
prefix={`location-${router.query.id}`} />
</>}
</>)}
</div>
<div className="panel-actions pt-12">
@ -270,3 +317,4 @@ export default function LocationForm() {
);
}

View File

@ -112,20 +112,35 @@ export default function Sidebar({ isSidebarOpen, toggleSidebar }) {
useEffect(() => {
const fetchLocations = async () => {
try {
if (session) { // Don't fetch locations if the user is not authenticated
// if (session)
{ // Don't fetch locations if the user is not authenticated
const response = await axiosInstance.get('/api/data/locations'); // Adjust the API endpoint as needed
const locationsData = response.data
.filter(location => location.isActive === true)
const subMenuLocations = response.data
.filter(location => location.isActive === true && location.isOnMainMenu === false)
.map(location => ({
text: location.name,
url: `/cart/locations/${location.id}`,
isOnMainMenu: location.isOnMainMenu,
}));
const mainMenuLocations = response.data.filter(l => l.isOnMainMenu === true)
.map(location => ({
key: location.name,
text: t('location.' + location.name + '.menu') || location.name,
url: location.url,
}));
console.log("locationsData: ", response.data);
console.log("subMenuLocations: ", subMenuLocations);
console.log("mainMenuLocations: ", mainMenuLocations);
// Find the "Locations" menu item and populate its children with locationsData
const menuIndex = sidemenu.findIndex(item => item.id === "locations");
if (menuIndex !== -1) {
sidemenu[menuIndex].children = locationsData;
sidemenu[menuIndex].children = subMenuLocations;
}
// insert main menu items after the locations (sidemenu[menuIndex+1])
sidemenu.splice(menuIndex + 1, 0, ...mainMenuLocations);
//setLocations(locationsData); // Optional, if you need to use locations elsewhere
}
} catch (error) {

View File

@ -34,6 +34,11 @@ const sidemenu = [
collapsable: true,
url: "/cart/locations",
},
{
id: "warehouse",
text: "Склад",
url: "/cart/locations/warehouse",
},
{
id: "cart-report",
text: "Отчет",

View File

@ -37,7 +37,8 @@
"location": {
"warehouse": {
"description": "- снимки как да се поставят количките\n- снимка с код за катинар\nвсеки може да всима/връща\n- преди да влизаме трябва да почистим краката.\n- зареждаме/почистваме извън\n- влизане/озлизане няма код\n- влизане/излизане има код",
"title": "СНЛАД"
"title": "СKЛАД",
"menu": "САД"
}
}
}

View File

@ -57,12 +57,14 @@ const ViewLocationPage: React.FC<ViewLocationPageProps> = ({ location }) => {
{location.name}
</button>
{/* Backup Location Tab */}
<button
className={`tab flex-1 text-lg py-2 px-4 ${activeTab === 'backupLocation' ? 'border-b-4 border-blue-500 text-blue-600 font-semibold' : 'text-gray-600 hover:text-blue-500'}`}
onClick={() => handleTabChange('backupLocation')}
>
При лошо време: <strong>{location.backupLocationName}</strong>
</button>
{location.backupLocationId !== null && (
<button
className={`tab flex-1 text-lg py-2 px-4 ${activeTab === 'backupLocation' ? 'border-b-4 border-blue-500 text-blue-600 font-semibold' : 'text-gray-600 hover:text-blue-500'}`}
onClick={() => handleTabChange('backupLocation')}
>
При лошо време: <strong>{location.backupLocationName}</strong>
</button>
)}
</div>
{/* Carousel */}
@ -98,6 +100,8 @@ const ViewLocationPage: React.FC<ViewLocationPageProps> = ({ location }) => {
);
};
const common = require("../../../src/helpers/common");
export const getServerSideProps: GetServerSideProps = async (context) => {
const axios = await axiosServer(context);
@ -126,17 +130,19 @@ export const getServerSideProps: GetServerSideProps = async (context) => {
});
};
const { data: location } = await axios.get(
`${process.env.NEXT_PUBLIC_PUBLIC_URL}/api/data/locations/${context.params.id}`
);
const prismaClient = common.getPrismaClient();
const location = await getLocation(prismaClient, context);
location.content = replacePlaceholders(location.content);
if (location.backupLocationId !== null) {
const { data: backupLocation } = await axios.get(
process.env.NEXT_PUBLIC_PUBLIC_URL + "/api/data/locations/" + location.backupLocationId
);
// const { data: backupLocation } = await axios.get(
// process.env.NEXT_PUBLIC_PUBLIC_URL + "/api/data/locations/" + location.backupLocationId
// );
const backupLocation = await prismaClient.location.findFirst({
where: {
id: location.backupLocationId,
},
});
location.backupLocationName = backupLocation.name;
location.backupLocationContent = backupLocation ? replacePlaceholders(backupLocation.content) : "";
location.backupLocationImages = backupLocation ? [backupLocation.picture1, backupLocation.picture2, backupLocation.picture3].filter(Boolean) : [];
@ -151,4 +157,30 @@ export const getServerSideProps: GetServerSideProps = async (context) => {
};
};
async function getLocation(prismaClient, context) {
// Try to parse the ID as a number
const numericId = parseInt(context.params.id);
let location;
// If it's a valid number, search by ID
if (!isNaN(numericId)) {
location = await prismaClient.location.findFirst({
where: {
id: numericId
}
});
}
// If no location found by ID or if ID is not numeric, search by name
if (!location) {
location = await prismaClient.location.findFirst({
where: {
name: context.params.id
}
});
}
return location;
}
export default ViewLocationPage;

View File

@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE `location` ADD COLUMN `isOnMainMenu` BOOLEAN NOT NULL DEFAULT false;

View File

@ -231,6 +231,7 @@ model Location {
backupLocationId Int?
backupLocation Location? @relation("BackupLocation", fields: [backupLocationId], references: [id])
BackupForLocations Location[] @relation("BackupLocation")
isOnMainMenu Boolean @default(false)
@@map("Location")
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 234 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 862 KiB