Compare commits

..

10 Commits

Author SHA1 Message Date
e280cd5a34 Refactor code structure for improved readability and maintainability 2025-07-18 15:17:57 +02:00
8363509210 Update pages/index.js 2025-07-18 09:16:08 +00:00
ce697548c6 Update pages/index.js 2025-07-18 09:08:57 +00:00
063e446983 Update Dockerfile
specify -bullseye for apt installs
2025-07-18 08:42:43 +00:00
8ef7fb9230 feat: Add MapComponent for displaying location on a map and TrackingPage for tracking shipments 2025-07-10 10:55:42 +02:00
32672387a6 fix: Update text descriptions in Cross and Home components for clarity 2025-07-01 12:07:20 +02:00
38970430a0 fix: Improve canvas error handling and update button functionality in Generator component 2025-07-01 11:51:19 +02:00
d3ecfae5df feat: Add Uziomy calculator page with grounding calculations and document generation
- Implemented Uziomy component for calculating grounding parameters.
- Added state management for input fields and results.
- Integrated DatePicker for date selection.
- Created functions for grounding calculations, document generation (DOCX), and DXF file generation.
- Enhanced UI with Tailwind CSS for better styling and responsiveness.
- Updated global styles to include Inter font and custom scrollbar styles.
- Configured Tailwind CSS to extend colors, fonts, and animations.
2025-07-01 11:43:34 +02:00
5c11a289df Add uziom generation script and update wet input documents
- Created a new Python script `uziom.py` for generating DXF drawings of grounding systems based on user input parameters.
- Added detailed documentation in `wet_input_15.docx` and `wet_input_3.docx` for the design of grounding systems, including calculations for resistance and resistivity measurements.
- Included placeholders for dynamic data insertion in the documents to facilitate project-specific customization.
2025-07-01 11:14:49 +02:00
e0d822adc8 Never before had a small typo like this one caused so much damage. 2022-05-12 15:23:44 +02:00
44 changed files with 11577 additions and 6888 deletions

5
.gitignore vendored
View File

@@ -41,7 +41,10 @@ a.py
*.txt
*.dxf
/public/*.dxf
/public/uploads/*
/pages/api/auth/*
/cypress
cypress.json
cypress.json
/tool-templates

465
ADDING_NEW_TOOLS_GUIDE.md Normal file
View File

@@ -0,0 +1,465 @@
# Adding New Tools and Functionality Guide
This document provides a comprehensive guide for adding new tools and functionality to the Wastpol web application. It's designed to help developers (including AI agents) integrate new tools from separate projects into the main application.
## Project Overview
The Wastpol application is a Next.js-based web application with the following key characteristics:
- **Framework**: Next.js 12 with React 17
- **Styling**: Tailwind CSS with custom UI components
- **Authentication**: NextAuth.js
- **Architecture**: Pages-based routing with component-driven design
- **UI Library**: Custom components + Evergreen UI + Heroicons
## Project Structure
```
web-app/
├── pages/ # Next.js pages (routes)
│ ├── api/ # API endpoints
│ ├── index.js # Main dashboard with tabs
│ ├── cross.js # Grid tool
│ ├── uziomy.js # Ground connections tool
│ ├── tracking.js # Package tracking
│ └── [other-tools].js # Additional tools
├── components/
│ ├── ui/ # Reusable UI components
│ │ ├── Layout.js # Main layout with navigation
│ │ └── components.js # UI component library
│ └── templates/ # Tool-specific components
├── styles/ # Global styles
├── public/ # Static assets
└── externals/ # External data files
```
## Adding a New Tool - Step by Step
### 1. Planning Phase
Before adding a new tool, define:
- **Tool Purpose**: What does the tool do?
- **Tool Name**: Short, descriptive name
- **Route**: URL path (e.g., `/new-tool`)
- **Icon**: Choose from Heroicons
- **Dependencies**: Any new packages needed
- **API Requirements**: Backend processing needs
### 2. Tool Integration Checklist
#### A. Create the Main Page Component
Create a new file: `pages/[tool-name].js`
**Template Structure:**
```javascript
import { useState, useCallback } from "react";
import { useSession, signIn } from "next-auth/react";
import Layout from "../components/ui/Layout";
import { Card, CardHeader, CardContent, CardTitle, CardDescription, Button, Alert } from "../components/ui/components";
import { [ToolIcon] } from '@heroicons/react/24/outline';
export default function NewTool() {
const { data: session } = useSession();
const [isLoading, setIsLoading] = useState(false);
// Tool-specific state variables
const [toolData, setToolData] = useState(null);
// Authentication check
if (!session) {
return (
<Layout title="Wastpol - New Tool">
<div className="flex items-center justify-center min-h-screen">
<Card className="w-full max-w-md">
<CardContent className="text-center p-6">
<h2 className="text-xl font-semibold mb-4">Wymagane logowanie</h2>
<Button onClick={() => signIn()}>
Zaloguj się
</Button>
</CardContent>
</Card>
</div>
</Layout>
);
}
return (
<Layout title="Wastpol - New Tool">
<div className="p-6 max-w-7xl mx-auto">
{/* Page Header */}
<div className="mb-8">
<h1 className="text-3xl font-bold text-gray-900 mb-2">
New Tool Name
</h1>
<p className="text-gray-600">
Tool description here
</p>
</div>
{/* Main Content */}
<Card>
<CardHeader>
<CardTitle className="flex items-center">
<[ToolIcon] className="w-6 h-6 mr-2" />
Tool Interface
</CardTitle>
<CardDescription>
Brief description of what this tool does
</CardDescription>
</CardHeader>
<CardContent>
{/* Tool-specific UI goes here */}
</CardContent>
</Card>
</div>
</Layout>
);
}
```
#### B. Add Navigation Entry
Update `components/ui/Layout.js`:
1. **Import the icon:**
```javascript
import {
// ...existing imports
[NewToolIcon]
} from '@heroicons/react/24/outline';
```
2. **Add to navigationItems array:**
```javascript
const navigationItems = [
// ...existing items
{ name: 'New Tool Name', href: '/new-tool', icon: NewToolIcon },
];
```
#### C. Create API Endpoint (if needed)
Create `pages/api/[tool-name].js`:
```javascript
import formidable from 'formidable';
import { getSession } from 'next-auth/react';
export const config = {
api: {
bodyParser: false,
},
};
export default async function handler(req, res) {
// Authentication check
const session = await getSession({ req });
if (!session) {
return res.status(401).json({ error: 'Unauthorized' });
}
if (req.method !== 'POST') {
return res.status(405).json({ error: 'Method not allowed' });
}
try {
// Tool-specific processing logic
const form = new formidable.IncomingForm();
const [fields, files] = await form.parse(req);
// Process the request
const result = await processToolData(fields, files);
res.status(200).json({
success: true,
data: result
});
} catch (error) {
console.error('Tool processing error:', error);
res.status(500).json({
error: 'Internal server error',
details: error.message
});
}
}
async function processToolData(fields, files) {
// Implement tool-specific logic here
return {};
}
```
#### D. Add Tool-Specific Components (Optional)
Create reusable components in `components/templates/[tool-name]/`:
```javascript
// components/templates/new-tool/ToolComponent.js
import { useState } from 'react';
import { Button, Alert } from '../../ui/components';
export default function ToolComponent({ onSubmit, isLoading }) {
const [inputData, setInputData] = useState('');
const handleSubmit = (e) => {
e.preventDefault();
onSubmit(inputData);
};
return (
<form onSubmit={handleSubmit} className="space-y-4">
{/* Tool-specific form elements */}
<Button
type="submit"
disabled={isLoading}
className="w-full"
>
{isLoading ? 'Processing...' : 'Submit'}
</Button>
</form>
);
}
```
### 3. Integration from Separate Project
#### A. Identify Components to Port
When integrating from a separate project:
1. **Identify core functionality files**
2. **Extract reusable components**
3. **Map dependencies and APIs**
4. **Identify data processing logic**
#### B. Dependency Management
1. **Check existing dependencies** in `package.json`
2. **Add new dependencies** if needed:
```bash
npm install [new-package]
```
3. **Update package.json** if manual editing is needed
#### C. Asset Management
1. **Static files**: Place in `public/` directory
2. **External data**: Place in `externals/` directory
3. **Uploads**: Use `public/uploads/` for user uploads
### 4. Common Integration Patterns
#### File Upload Pattern
```javascript
import { FileUploader, FileCard } from "evergreen-ui";
const [files, setFiles] = useState([]);
const handleChange = useCallback((files) => setFiles([files[0]]), []);
```
#### API Call Pattern
```javascript
import axios from "axios";
const handleSubmit = async (data) => {
setIsLoading(true);
try {
const formData = new FormData();
formData.append("data", JSON.stringify(data));
const response = await axios.post("/api/new-tool", formData);
setResult(response.data);
} catch (error) {
setError(error.message);
} finally {
setIsLoading(false);
}
};
```
#### State Management Pattern
```javascript
const [isLoading, setIsLoading] = useState(false);
const [result, setResult] = useState(null);
const [error, setError] = useState(null);
```
### 5. Styling Guidelines
#### Use Existing UI Components
```javascript
import {
Card,
CardHeader,
CardContent,
CardTitle,
CardDescription,
Button,
Alert
} from "../components/ui/components";
```
#### Consistent Layout Classes
- **Container**: `p-6 max-w-7xl mx-auto`
- **Page Header**: `mb-8`
- **Title**: `text-3xl font-bold text-gray-900 mb-2`
- **Description**: `text-gray-600`
- **Cards**: Use existing Card components
- **Spacing**: `space-y-4` for form elements
### 6. Error Handling Best Practices
#### Client-Side Error Handling
```javascript
const [error, setError] = useState(null);
// Clear error on new action
const clearError = () => setError(null);
// Display errors
{error && (
<Alert variant="destructive" className="mb-4">
{error}
</Alert>
)}
```
#### Server-Side Error Handling
```javascript
try {
// Process request
} catch (error) {
console.error('Tool error:', error);
return res.status(500).json({
error: 'Processing failed',
details: process.env.NODE_ENV === 'development' ? error.message : undefined
});
}
```
### 7. Testing Integration
#### Manual Testing Checklist
- [ ] Tool loads without errors
- [ ] Authentication works correctly
- [ ] Navigation item appears and works
- [ ] File uploads work (if applicable)
- [ ] API endpoints respond correctly
- [ ] Results display properly
- [ ] Error handling works
- [ ] Mobile responsiveness
- [ ] Loading states display correctly
#### Console Testing
1. Check browser console for errors
2. Verify API responses in Network tab
3. Test with different user roles
### 8. AI Agent Instructions
When an AI agent is adding a new tool:
#### Information to Gather
1. **Tool description and purpose**
2. **Required input/output formats**
3. **Processing logic requirements**
4. **UI/UX preferences**
5. **Integration points with existing system**
#### Step-by-Step Process
1. **Analyze the existing project structure**
2. **Identify the closest existing tool for reference**
3. **Create the main page component**
4. **Add navigation entry**
5. **Create API endpoint if needed**
6. **Test the integration**
7. **Handle any errors or conflicts**
#### Code Quality Standards
- Follow existing naming conventions
- Use consistent styling patterns
- Implement proper error handling
- Add loading states for async operations
- Ensure responsive design
- Maintain authentication requirements
### 9. Example Tool Addition
Here's how to add a hypothetical "Document Generator" tool:
#### Step 1: Create `pages/document-generator.js`
```javascript
import { useState } from "react";
import { useSession, signIn } from "next-auth/react";
import Layout from "../components/ui/Layout";
import { Card, CardHeader, CardContent, CardTitle } from "../components/ui/components";
import { DocumentTextIcon } from '@heroicons/react/24/outline';
export default function DocumentGenerator() {
const { data: session } = useSession();
// Component implementation...
}
```
#### Step 2: Update `components/ui/Layout.js`
```javascript
import { DocumentTextIcon } from '@heroicons/react/24/outline';
const navigationItems = [
// ...existing items
{ name: 'Generator dokumentów', href: '/document-generator', icon: DocumentTextIcon },
];
```
#### Step 3: Create `pages/api/document-generator.js`
```javascript
export default async function handler(req, res) {
// API implementation...
}
```
### 10. Maintenance and Updates
#### Version Control
- Commit changes with descriptive messages
- Tag releases for major tool additions
- Document breaking changes
#### Documentation Updates
- Update this guide when patterns change
- Document new dependencies
- Keep API documentation current
#### Performance Considerations
- Monitor bundle size with new dependencies
- Optimize images and assets
- Consider lazy loading for heavy components
---
## Quick Reference Commands
```bash
# Install new dependency
npm install [package-name]
# Run development server
npm run dev
# Build for production
npm run build
# Deploy changes
npm run deploy
```
## Common File Locations
- **New page**: `pages/[tool-name].js`
- **API endpoint**: `pages/api/[tool-name].js`
- **Components**: `components/templates/[tool-name]/`
- **Navigation**: `components/ui/Layout.js`
- **Styles**: `styles/globals.css`
- **Assets**: `public/`
This guide should provide everything needed to successfully integrate new tools into the Wastpol application while maintaining consistency and code quality.

21
Dockerfile Normal file
View File

@@ -0,0 +1,21 @@
# Build stage
FROM node:16 AS builder
WORKDIR /panel
COPY . .
RUN npm install && npm run build
# Final stage: Node.js + Python + ezdxf
FROM node:16-bullseye
# Install Python and pip
RUN apt-get update && apt-get install -y python3 python3-pip && \
pip3 install --no-cache-dir ezdxf && \
apt-get clean && rm -rf /var/lib/apt/lists/*
# Copy Node app
WORKDIR /panel
COPY --from=builder /panel ./
RUN npm install --omit=dev
EXPOSE 3000
CMD ["npm", "run", "start"]

View File

@@ -0,0 +1,153 @@
# Power System Designer - Integration Summary
## ✅ **Fixes and Improvements Implemented**
### **1. Fixed Object Selection**
- **Issue**: Selection tool didn't work properly
- **Fix**:
- Implemented proper `findObjectAt()` function with correct hit detection
- Added visual feedback for selected objects (blue glow)
- Improved click detection accuracy with 20px radius
### **2. Added Object Dragging**
- **Issue**: Objects couldn't be moved
- **Fix**:
- Implemented `handleCanvasMouseDown`, `handleCanvasMouseMove`, and `handleCanvasMouseUp` event handlers
- Added drag state management with `isDragging` and `dragOffset`
- Objects snap to 20px grid while dragging
- Visual feedback during dragging operations
### **3. Fixed Cable Connection Tool**
- **Issue**: Cable connection tool didn't work at all
- **Fix**:
- Proper two-click connection workflow
- Visual feedback showing selected connection source (orange highlight)
- Connection status indicator overlay
- Automatic cable length calculation based on object positions
- Prevention of duplicate connections and self-connections
- Error messages for invalid connections
### **4. Enhanced Object Data Structure**
- **Issue**: Objects didn't have all necessary information
- **Fix**:
- Added comprehensive object properties:
- `typeDescription` - Custom type descriptions
- `load` - Current load vs rated power
- `voltage` - Operating voltage
- `current` - Current flow (for calculations)
- Position tracking (`x`, `y`)
- Proper default values for each object type
### **5. Improved Cable Data Structure**
- **Enhanced Cable Properties**:
- `resistance` - Electrical resistance based on cable type/size
- Better cable type handling (underground vs overhead)
- Automatic resistance calculation
- Improved cable length calculation
### **6. Enhanced User Interface**
#### **Object Properties Panel**:
- Object type indicator with ID
- Position display (X, Y coordinates)
- Type-specific property fields
- Enhanced transformer properties (primary/secondary voltage, power rating)
- Load vs power rating for distribution points
- Custom type descriptions
#### **Cable Properties Panel**:
- Connection source/destination display
- Electrical parameters (resistance, installation type)
- Cable type selection with proper underground/overhead distinction
- Cross-section selection with standard sizes
- Delete cable functionality
#### **Visual Improvements**:
- Better object highlighting (selected vs connection source)
- Improved cable rendering with proper colors:
- Purple: Underground cables (YAKY, NA2XY-J)
- Orange: Overhead cables (AL, AsXSn)
- Dashed lines for overhead cables
- Cable labels with background for better readability
- Connection status indicator overlay
### **7. Added Interaction Features**
#### **Mouse Controls**:
- **Left Click**: Select/place objects
- **Drag**: Move selected objects (with grid snapping)
- **Connect Mode**: Two-click cable connection workflow
#### **Keyboard Shortcuts**:
- **Delete/Backspace**: Delete selected object or cable
- **Escape**: Clear selection and return to select mode
#### **Object Management**:
- Delete objects (with automatic cable cleanup)
- Delete cables individually
- Proper state management for selections
### **8. Error Handling and Validation**
- Transformer limit enforcement (only one allowed)
- Duplicate connection prevention
- Self-connection prevention
- Error messages with clear descriptions
- Input validation for numeric fields
### **9. Technical Improvements**
#### **Event System**:
- Proper mouse event handling with coordinate transformation
- Grid snapping for precise placement
- Drag offset calculation for smooth object movement
#### **State Management**:
- Clean separation of object and cable selections
- Proper state updates with React patterns
- Automatic re-rendering on state changes
#### **Performance**:
- Efficient hit detection algorithms
- Optimized canvas rendering
- Proper event listener cleanup
## **🎯 Key Features Now Working**
1. **✅ Object Selection**: Click objects to select and edit properties
2. **✅ Object Movement**: Drag objects to new positions with grid snapping
3. **✅ Cable Connections**: Two-click workflow to connect objects
4. **✅ Property Editing**: Comprehensive property panels for all object types
5. **✅ Visual Feedback**: Clear indication of selected items and connection states
6. **✅ Delete Functions**: Remove objects and cables with keyboard shortcuts
7. **✅ Data Persistence**: Export/import with complete object data
8. **✅ Electrical Calculations**: Ready for voltage drop analysis
## **🔧 Usage Instructions**
### **Adding Objects**:
1. Click object type button in toolbar (Transformer, Box, Pole, End Connection)
2. Click on canvas to place object
3. Object automatically snaps to grid
### **Selecting and Moving**:
1. Click "Wybierz" (Select) tool
2. Click object to select (blue highlight appears)
3. Drag selected object to move (snaps to grid)
### **Connecting with Cables**:
1. Click "Połącz kablem" (Connect with Cable) tool
2. Click first object (orange highlight appears)
3. Click second object to create cable connection
4. Cable appears with automatic length calculation
### **Editing Properties**:
1. Select object or cable
2. Use properties panel on right to edit all parameters
3. Changes are saved automatically
### **Deleting Items**:
1. Select object or cable
2. Press Delete/Backspace key, OR
3. Use "Usuń" (Delete) button in properties panel
The power system designer is now fully functional with professional-grade features for electrical system design and analysis!

132
UI_REDESIGN_PROPOSAL.md Normal file
View File

@@ -0,0 +1,132 @@
# Wastpol UI Redesign Proposal
## Overview
This UI redesign modernizes the Wastpol electrical engineering application with a contemporary, professional interface while maintaining all existing functionality.
## Key Improvements
### 🎨 **Visual Design**
- **Modern Color Palette**: Professional blue-based theme with proper contrast ratios
- **Typography**: Inter font family for improved readability
- **Consistent Spacing**: Systematic spacing scale using Tailwind CSS
- **Enhanced Shadows**: Subtle depth with layered shadow system
- **Better Visual Hierarchy**: Clear information architecture with proper heading levels
### 📱 **Responsive Layout**
- **Mobile-First Design**: Optimized for all screen sizes
- **Sidebar Navigation**: Collapsible sidebar for desktop, drawer for mobile
- **Adaptive Grid**: Responsive card layouts that adjust to screen size
- **Touch-Friendly**: Proper touch targets for mobile devices
### 🎯 **User Experience**
- **Simplified Navigation**: Clear, icon-based navigation with active states
- **Progress Indicators**: Loading states and progress feedback
- **Better Form Design**: Improved input styling with proper validation states
- **Interactive Elements**: Hover effects and smooth transitions
- **Card-Based Layout**: Organized content in digestible sections
### ⚡ **Performance & Accessibility**
- **Semantic HTML**: Proper heading structure and ARIA labels
- **Keyboard Navigation**: Full keyboard accessibility
- **Color Contrast**: WCAG 2.1 AA compliant color combinations
- **Focus Management**: Clear focus indicators
- **Screen Reader Support**: Proper labeling and descriptions
## New Components
### Layout System
- **Layout.js**: Unified layout component with responsive sidebar
- **components.js**: Reusable UI components (Card, Button, Input, etc.)
### Key Features
#### 1. Unified Navigation
- Logo and branding in header
- Clear section navigation (Przekrój, Siatka, Uziomy)
- User authentication status
- Responsive mobile menu
#### 2. Enhanced Cards
- Clean card design with headers and content sections
- Proper spacing and typography
- Action buttons with loading states
- Status indicators and badges
#### 3. Improved Forms
- Better input styling with floating labels
- Proper validation states
- Radio groups and select inputs
- Date picker integration
#### 4. Better Feedback
- Loading spinners for async operations
- Success/error alerts with proper styling
- Progress indicators
- Toast notifications
## Technical Implementation
### Dependencies Added
- `@heroicons/react`: Modern icon system
- `@tailwindcss/forms`: Enhanced form styling
### File Structure
```
components/
ui/
Layout.js # Main layout wrapper
components.js # Reusable UI components
templates/
generator_new.js # Updated terrain profile generator
manual_new.js # Updated manual input component
pages/
cross_new.js # Updated grid generator page
uziomy_new.js # Updated grounding calculator page
```
### Color System
- **Primary**: Blue (#3B82F6) for main actions
- **Secondary**: Gray tones for secondary elements
- **Success**: Green for positive states
- **Warning**: Yellow for attention states
- **Error**: Red for error states
### Typography Scale
- **Headings**: Inter font with proper weight hierarchy
- **Body**: 14px base with good line height
- **Code**: Monospace for technical data input
## Benefits
### For Users
- **Easier Navigation**: Intuitive interface reduces learning curve
- **Better Mobile Experience**: Responsive design works on all devices
- **Faster Workflows**: Streamlined forms and clear action buttons
- **Professional Appearance**: Modern design builds trust and credibility
### For Developers
- **Component Reusability**: Consistent design system
- **Maintainable Code**: Clean component architecture
- **Modern Stack**: Latest design practices and tools
- **Scalable System**: Easy to extend and modify
## Implementation Notes
### Backward Compatibility
- Original files preserved (can switch back if needed)
- New files use `_new.js` suffix for testing
- API endpoints remain unchanged
- All functionality preserved
### Migration Path
1. Test new components in development
2. Gradually replace old components
3. Update remaining templates to match new design
4. Remove old components once migration complete
### Browser Support
- Modern browsers (Chrome, Firefox, Safari, Edge)
- Mobile browsers (iOS Safari, Chrome Mobile)
- Progressive enhancement for older browsers
This redesign transforms Wastpol into a modern, professional application that enhances user productivity while maintaining all existing engineering capabilities.

39
archive/README.md Normal file
View File

@@ -0,0 +1,39 @@
# Archive - Old UI Components
This folder contains the original versions of components and pages that were replaced during the UI redesign.
## Archived Files
### Pages
- `cross_old.js` - Original cross-section generator page
- `uziomy_old.js` - Original grounding calculator page
### Components
- `templates/generator_old.js` - Original profile generator component
- `templates/manual_old.js` - Original manual input component
## What Changed
The archived files used the old UI system with:
- Evergreen UI components
- Older styling approach
- Less responsive design
- Basic user interface
The new versions (now default) feature:
- Modern Tailwind CSS design system
- Responsive layout with sidebar navigation
- Custom UI components (`components/ui/`)
- Improved user experience
- Heroicons v2 integration
- Better mobile support
## Restoration
If you need to restore an old version, you can:
1. Copy the desired file from this archive
2. Rename it to remove the `_old` suffix
3. Replace the current version in the main project
## Date Archived
Archived on: July 1, 2025

View File

@@ -0,0 +1,231 @@
import { useState, useEffect } from "react";
import { useRouter } from "next/router";
import {
Pane,
TextInputField,
TextareaField,
Button,
BuildIcon,
toaster,
Alert,
RadioGroup,
} from "evergreen-ui";
import axios from "axios";
import Footer from "./footer";
export default function Generator() {
const [profil, setProfil] = useState();
const [args, setArgs] = useState({
scale: 200,
elementOne: 0,
elementTwo: 0,
});
const [ElementOneOptions] = useState([
{ label: "Nic", value: "0" },
{ label: "Słup", value: "1" },
{ label: "Dom", value: "2" },
]);
const [ElementTwoOptions] = useState([
{ label: "Nic", value: "0" },
{ label: "Słup", value: "1" },
{ label: "Dom", value: "2" },
]);
const { query } = useRouter();
useEffect(() => {
if (query.external == "tru") {
axios
.post("/api/readtext", {
id: query.id,
})
.then(function (response) {
setProfil(response.data.data);
document.getElementById("textarea-1").value = response.data.data;
console.log(response.data.data);
});
}
}, []);
const getPath = (e, path) => {
let newLines = [];
for (let line of path.split("\n")) {
if (line[0] == "P") continue;
if (line[0] == "S") continue;
if (line[0] == "X") continue;
newLines.push(line);
}
let xs = [];
let ys = [];
let zs = [];
let toti = 0;
let al = 0;
for (let line of newLines) {
al += 1;
let theLine = line.split(",");
//console.log(theLine)
if (
xs &&
parseFloat(theLine[0]) == xs[-1] &&
parseFloat(theLine[1]) == ys[-1]
)
continue;
if (al > 2) {
let dist =
((xs[xs.length - 1] - xs[xs.length - 2]) ** 2 +
(ys[ys.length - 1] - ys[ys.length - 2]) ** 2) **
0.5;
toti += Math.round(dist);
}
xs.push(parseFloat(theLine[0]));
ys.push(parseFloat(theLine[1]));
zs.push(parseFloat(theLine[2]));
}
let prevLine = ["0", "0", "0"];
let scaleFactor = 100000 / 2000;
const canvas = document.getElementById("canvas");
if (!canvas.getContext) {
console.log("canvas err");
toaster.warning("canvas err");
return;
}
const ctx = canvas.getContext("2d");
ctx.clearRect(0, 0, 700, 700);
// set line stroke and line width
ctx.strokeStyle = "red";
ctx.lineWidth = 2;
let totalH = Math.max(...zs);
let minH = Math.min(...zs);
for (let line = 0; line < xs.length - 1; line++) {
let theLine = [xs[line], ys[line], zs[line]];
let x = parseFloat(theLine[0]) - parseFloat(prevLine[0]);
let y = parseFloat(theLine[1]) - parseFloat(prevLine[1]);
let x1 = line * scaleFactor;
let y1 = zs[line];
let x2 = (line + 1) * scaleFactor;
let y2 = zs[line + 1];
//console.log(x1, y1, x2, y2);
let b0 = 0;
let b1 = 500;
let z1 = b0 + (b1 - b0) * ((y1 - totalH) / (minH - totalH));
let z2 = b0 + (b1 - b0) * ((y2 - totalH) / (minH - totalH));
//b0 + (b1 - b0) * ((x1-0)/(toti*scaleFactor-0));
let x12 = b0 + (b1 - b0) * ((x1 - 0) / (toti * scaleFactor));
let x22 = b0 + (b1 - b0) * ((x2 - 0) / (toti * scaleFactor));
//console.log(x12);
ctx.beginPath();
ctx.moveTo(x12, z1);
ctx.lineTo(x22, z2);
ctx.stroke();
}
};
const parsePreview = (e) => {
// console.log(dxf);
};
const py = (e, profil, args) => {
e.preventDefault();
axios
.post("/api/spawn", {
profil: profil,
arguments: args,
})
.then(function (response) {
console.log(response);
if (response.data.toString().startsWith("Py Error")) {
toaster.danger(response.data);
return;
}
toaster.warning(response.data.data);
document.getElementById("down").download =
response.data.filename.toString() + ".dxf";
document.getElementById("down").click();
})
.catch(function (error) {
console.log(error);
});
};
return (
<div className="flex xl:flex-row flex-col">
<div className="flex flex-col">
<Pane width={480}>
<TextareaField
id="textarea-1"
label="Dane z Geoportalu:"
placeholder="Próbkowanie: 1 ..."
onChange={(e) => {
//console.log(e.target.value);
setProfil(e.target.value);
parsePreview(e);
getPath(e, e.target.value);
}}
/>
<TextInputField
label="Skala:"
placeholder="200"
width={100}
onChange={(e) => {
console.log(e.target.value);
setArgs({ ...args, scale: e.target.value });
}}
/>
<RadioGroup
label="Lewa"
value={args.elementOne}
options={ElementOneOptions}
onChange={(event) => {
setArgs({ ...args, elementOne: event.target.value });
}}
/>
<RadioGroup
label="Prawa"
value={args.elementTwo}
options={ElementTwoOptions}
onChange={(event) => {
setArgs({ ...args, elementTwo: event.target.value });
}}
/>
<Button
marginY={8}
marginRight={12}
appearance="default"
iconAfter={BuildIcon}
onClick={(e) => {
if (document.getElementById("textarea-1").value == "") {
toaster.danger("Pole danych nie może być puste");
} else if (
!document
.getElementById("textarea-1")
.value.startsWith("Próbkowanie")
) {
toaster.danger("Błędne dane");
} else {
py(e, profil, args);
}
}}
>
Generuj
</Button>
</Pane>
<a href="test.dxf" download="test.dxf" id="down">
{" "}
</a>
</div>
<Pane className="ml-8 shadow-md rounded-md">
<canvas id="canvas" height="500" width="700"></canvas>
</Pane>
</div>
);
}

View File

@@ -0,0 +1,157 @@
import { useState, useEffect } from "react";
import {
Pane,
TextInputField,
TextareaField,
Button,
toaster,
Alert,
TrashIcon,
Icon,
} from "evergreen-ui";
import axios from "axios";
import Footer from "./footer";
import Generator from "./generator";
export default function Manual() {
const [points, setPoints] = useState([{ id: 0, len: 0, height: 0 }]);
const [args, setArgs] = useState({ scale: 200 });
//useEffect(() => {
//do something here
// }, [points]);
const reIndex = () => {
let newId = 0;
let newPoints = [];
for (let point of points) {
point.id = newId;
newPoints.push(point);
newId += 1;
}
setPoints([...newPoints]);
};
const generation = (e) => {
let pointString = "Próbkowanie: 1\nSegment 0: w dół\nX: Y, Z";
for (let point of points) {
pointString += "\n";
pointString += Number(point.len) * 1.0;
pointString += ", 1.0, ";
pointString += point.height;
}
console.log(pointString);
py(e, pointString, args);
};
const py = (e, profil, args) => {
e.preventDefault();
axios
.post("/api/spawn", {
profil: profil,
arguments: args,
})
.then(function (response) {
console.log(response);
if (response.data.toString().startsWith("Py Error")) {
toaster.danger(response.data);
return;
}
toaster.warning(response.data.data);
document.getElementById("down").download =
response.data.filename.toString() + ".dxf";
document.getElementById("down").click();
})
.catch(function (error) {
console.log(error);
});
};
return (
<div className="flex xl:flex-row flex-col">
<div className="flex flex-col">
<Pane width={480}>
{points.map((point, index) => (
<Pane className="flex flex-row items-center">
<TextInputField
id={`len-${index}`}
placeholder="0 m"
onChange={(e) => {
console.log(e.target.value);
let newPoints = points;
newPoints[index].len = e.target.value;
setPoints([...newPoints]);
}}
value={point.len}
></TextInputField>
<TextInputField
id={`h-${index}`}
placeholder="0 m n.p.m."
onChange={(e) => {
console.log(e.target.value);
let newPoints = points;
newPoints[index].height = e.target.value;
setPoints([...newPoints]);
}}
value={point.height}
onKeyDown={(e) => {
if (e.key == "Enter") {
setPoints([
...points,
{
id: points.length,
len: Number(points[points.length - 1].len) + 1,
height: 0,
},
]);
let nextDiv = e.target.parentNode.parentNode.nextSibling;
nextDiv.childNodes[1].childNodes[1].focus()
nextDiv.childNodes[1].childNodes[1].setSelectionRange(0, 1)
}
}}
></TextInputField>
<Button
className="mb-5"
appearance="minimal"
onClick={() => {
let newPoints = points;
newPoints.splice(index, 1);
setPoints([...newPoints]);
reIndex();
}}
>
<Icon icon={TrashIcon} color="danger"></Icon>
</Button>
</Pane>
))}
</Pane>
<Button
onClick={() => {
setPoints([
...points,
{
id: points.length,
len: Number(points[points.length - 1].len) + 1,
height: 0,
},
]);
}}
>
Dodaj
</Button>
<Button
appearance="primary"
onClick={(e) => {
generation(e);
}}
>
Generuj
</Button>
</div>
</div>
);
}

178
archive/pages/cross_old.js Normal file
View File

@@ -0,0 +1,178 @@
import { useState, useCallback } from "react";
import { useSession, signIn } from "next-auth/react";
import Layout from "../components/ui/Layout";
import { Card, CardHeader, CardContent, CardTitle, CardDescription, Button, Alert } from "../components/ui/components";
import {
FileUploader,
FileCard,
} from "evergreen-ui";
import { CloudArrowUpIcon as CloudUploadIcon, ArrowDownTrayIcon as DownloadIcon, Squares2X2Icon as GridIcon } from '@heroicons/react/24/outline';
import axios from "axios";
export default function Cross() {
const { data: session } = useSession();
const [fileData, setFileData] = useState(null);
const handleDownload = () => {
console.log("down");
if (fileData) {
console.log("load");
// const link = document.createElement("a");
// link.href = `data:application/octet-stream;base64,${fileData}`;
// link.download = "plik.dxf";
// link.click();
document.getElementById("down").download = fileData.filename;
console.log(fileData.filename)
document.getElementById("down").href = "cross.dxf";
console.log("cross.dxf")
document.getElementById("down").click();
}
};
const [files, setFiles] = useState([]);
const [fileRejections, setFileRejections] = useState([]);
const handleChange = useCallback((files) => setFiles([files[0]]), []);
const handleRejected = useCallback(
(fileRejections) => setFileRejections([fileRejections[0]]),
[]
);
const handleRemove = useCallback(() => {
setFiles([]);
setFileRejections([]);
}, []);
const handleSubmit = async (event) => {
event.preventDefault();
let file = files[0];
if (!file) {
// Handle error if no file is selected
return;
}
const formData = new FormData();
formData.append("file", file);
axios
.post("/api/upload", formData, {
headers: {
"Content-Type": "multipart/form-data",
},
})
.then((response) => {
console.log(response.data);
if (response.data.toString().startsWith("Py Error")) {
toaster.danger(response.data);
return;
}
toaster.warning(response.statusText);
console.log(response.data)
setFileData(response.data);
document.getElementById("download").disabled = false;
// document.getElementById("down").download =
// response.data.filename.toString().split(".")[0].split("-")[1]+"_s.dxf";
// document.getElementById("down").href =
// "uploads/"+response.data.filename.toString().split(".")[0]+"_s.dxf";
// document.getElementById("down").click();
})
.catch((error) => {
console.error(error);
});
};
if (session) {
return (
<div className="">
<Head>
<title>Wastpol</title>
</Head>
<div className="flex md:flex-row flex-col justify-between px-8 mt-2">
<Nav />
<UserTop session={session} />
</div>
<main className="flex flex-1 flex-col items-center">
<FileUploader
label="Prześlij plik"
description="Możesz dodać 1 plik, max 50 MB."
maxSizeInBytes={50 * 1024 ** 2}
maxFiles={1}
onChange={handleChange}
onRejected={handleRejected}
renderFile={(file) => {
const { name, size, type } = file;
const fileRejection = fileRejections.find(
(fileRejection) => fileRejection.file === file
);
const { message } = fileRejection || {};
return (
<FileCard
key={name}
isInvalid={fileRejection != null}
name={name}
onRemove={handleRemove}
sizeInBytes={size}
type={type}
validationMessage={message}
/>
);
}}
values={files}
/>
<Button
marginY={8}
marginRight={12}
appearance="default"
iconAfter={BuildIcon}
onClick={(e) => {
console.log("louding begins");
console.log(files);
document.getElementById("download").disabled = false;
handleSubmit(e);
}}
>
Dodaj siatkę
</Button>
<Button
id="download"
marginY={8}
marginRight={12}
appearance="default"
iconAfter={BuildIcon}
// disabled={true}
onClick={handleDownload}
>
Pobierz
</Button>
<a href="the.dxf" download="the.dxf" id="down">
{" "}
</a>
</main>
</div>
);
}
return (
<div className="grid place-items-center h-screen">
<Head>
<title>Wastpol</title>
</Head>
<div className="flex flex-col justify-center">
<h2 className="p-2">Nie zalogowano</h2>
<br></br>
<Button
onClick={() => signIn()}
appearance="primary"
// className="p-2 bg-slate-200 rounded-md"
>
Zaloguj
</Button>
</div>
</div>
);
}

551
archive/pages/uziomy_old.js Normal file
View File

@@ -0,0 +1,551 @@
import Head from "next/head";
import styles from "../styles/Home.module.css";
import Nav from "../components/templates/nav";
import UserTop from "../components/templates/userTop";
import { useSession, signIn, signOut } from "next-auth/react";
import { useState, useCallback } from "react";
import {
Pane,
TextInputField,
TextareaField,
Button,
BuildIcon,
toaster,
Alert,
FileUploader,
FilePicker,
FileCard,
RadioGroup,
Autocomplete,
} from "evergreen-ui";
import PizZip from "pizzip";
import Docxtemplater from "docxtemplater";
import DatePicker from "react-datepicker";
import { registerLocale, setDefaultLocale } from "react-datepicker";
import "react-datepicker/dist/react-datepicker.css";
import pl from "date-fns/locale/pl";
registerLocale("pl", pl);
export default function Rezy() {
const { data: session } = useSession();
const [currentStep, setCurrentStep] = useState(1);
const [ground, setGround] = useState({
wet_coef: 0,
resistivity: 0,
resistance: 0,
measure_dist: 0,
rod_len: 0,
rod_num: 0,
rod_coef: 0,
hor_len: 0,
result_v: 0,
result_h: 0,
result: 0,
wszrg_h: 0,
wszrg_v: 0,
wanted: 0,
date: undefined,
no: 0,
pr_title: "Budowa przyłącza kablowego nN",
in_city: undefined,
commune: undefined,
all_parcels: undefined,
target_parcel: undefined,
geo_data: undefined,
object: "Przyłącz kablowy nN",
objValue1: "proj.",
objName: undefined,
});
// Function to go to the next step
const goToNextStep = () => {
if (currentStep < 3) {
setCurrentStep(currentStep + 1);
}
};
// Function to go to the previous step
const goToPreviousStep = () => {
if (currentStep > 1) {
setCurrentStep(currentStep - 1);
}
};
const [date, setDate] = useState(null);
const [calDate, setCalDate] = useState(null);
const [options] = useState([
{ label: "5 Ω", value: "5" },
{ label: "10 Ω", value: "10" },
{ label: "15 Ω", value: "15" },
{ label: "30 Ω", value: "30" },
]);
const [neededValue, setNeededValue] = useState("5");
const [resHValue, setResHValue] = useState("88");
const [resVValue, setResVValue] = useState("89");
const [objOptions1] = useState([
{ label: "proj.", value: "proj." },
{ label: "istn.", value: "istn." },
]);
const [objValue1, setObjValue1] = useState("proj.");
function getRandomInt(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min + 1)) + min;
}
function parseDate(dateString) {
console.log(dateString);
const parts = dateString.split(".");
const day = parseInt(parts[0], 10);
const month = parseInt(parts[1], 10) - 1; // Months are 0-indexed in JavaScript Dates
const year = parseInt(parts[2], 10);
return new Date(year, month, day);
}
function getGrounding(wanted, wszrg_h, wszrg_v, date) {
const dateObject = parseDate(date);
const month = dateObject.getMonth() + 1; // JavaScript months are 0-indexed
const wet_coef = month >= 6 && month <= 9 ? 1.2 : 1.6;
const rod_len = wanted === 30 ? 2 : 3;
const resistivity_h = wszrg_h / wet_coef;
const measure_dist_h = 1;
const resistance_h = resistivity_h / (2 * Math.PI * measure_dist_h);
const resistivity_v = wszrg_v / wet_coef;
const measure_dist_v = 1 + rod_len;
const resistance_v = resistivity_v / (2 * Math.PI * measure_dist_v);
const result_v =
(wszrg_v / (2 * Math.PI * rod_len)) *
(Math.log((8 * rod_len) / 0.016) - 1);
let rod_num = 2; //minimum 2 rods
let hor_len = 1 + (rod_num - 1) * rod_len * 2;
let result_h =
(wszrg_h / (2 * Math.PI * hor_len)) *
Math.log((hor_len * hor_len) / (1 * 0.0191));
let rod_coef =
Math.pow(rod_num, 4) * 0.00002 -
Math.pow(rod_num, 3) * 0.0009 +
Math.pow(rod_num, 2) * 0.0137 -
rod_num * 0.0981 +
1.0468;
let result =
(result_v * result_h) /
(result_v * rod_coef + rod_num * result_h * rod_coef);
while (result > wanted) {
rod_num += 1;
hor_len = 1 + (rod_num - 1) * rod_len * 2;
result_h =
(wszrg_h / (2 * Math.PI * hor_len)) *
Math.log((hor_len * hor_len) / (1 * 0.0191));
rod_coef =
Math.pow(rod_num, 4) * 0.00002 -
Math.pow(rod_num, 3) * 0.0009 +
Math.pow(rod_num, 2) * 0.0137 -
rod_num * 0.0981 +
1.0468;
result =
(result_v * result_h) /
(result_v * rod_coef + rod_num * result_h * rod_coef);
console.log(result, rod_num);
}
console.log(result, rod_num);
return {
wet_coef: wet_coef,
resistivity_h: resistivity_h.toFixed(2),
resistance_h: resistance_h.toFixed(2),
measure_dist_h: measure_dist_h,
resistivity_v: resistivity_v.toFixed(2),
resistance_v: resistance_v.toFixed(2),
measure_dist_v: measure_dist_v,
rod_len: rod_len,
rod_num: rod_num,
rod_coef: rod_coef.toFixed(2),
hor_len: hor_len,
result_v: result_v.toFixed(2),
result_h: result_h.toFixed(2),
result: result.toFixed(2),
wszrg_h: wszrg_h,
wszrg_v: wszrg_v,
wanted: neededValue,
};
}
const generateDocument = async (ground) => {
const data = {
...ground,
resisted_object: ground.objValue1 + " " + ground.objName,
};
try {
const response = await fetch("/api/generateDocx", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(data),
});
if (!response.ok) {
throw new Error(`Error: ${response.status}`);
}
// Convert the response to a blob and download it
const blob = await response.blob();
const downloadUrl = window.URL.createObjectURL(blob);
const link = document.createElement("a");
link.href = downloadUrl;
link.download = "opis.docx";
document.body.appendChild(link);
link.click();
link.remove();
} catch (error) {
console.error("Failed to generate document:", error);
}
};
const generateDxf = async () => {
// Data that you want to send to the backend
var dateParts = ground.date.split(".");
// Extract day, month, and year
var day = parseInt(dateParts[0], 10);
var month = parseInt(dateParts[1], 10);
var year = parseInt(dateParts[2], 10);
// Format the result
var formattedDate = month.toString().padStart(2, "0") + "." + year;
const inputData = {
args: [
ground.objValue1 + ground.objName,
ground.pr_title,
ground.object,
ground.in_city +
", " +
ground.commune +
", dz. nr " +
ground.all_parcels,
formattedDate,
ground.hor_len,
ground.rod_len,
],
};
// object1 = args[0] #ZK2a-1P
// object2 = args[1] #Budowa przyłącza
// object3 = args[2] #Przyłącze kablowe
// adres = args[3]
// date = args[4]
// len_h = int(args[5])
// len_v = int(args[6])
try {
const response = await fetch("/api/generateDxf", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(inputData),
});
if (!response.ok) {
throw new Error("Response was not ok.");
}
// Download the response (the output file)
const blob = await response.blob();
const downloadUrl = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = downloadUrl;
a.download = "uziom.dxf";
document.body.appendChild(a);
a.click();
a.remove();
} catch (error) {
console.error("There was an error:", error);
}
};
if (session) {
return (
<div className="">
<Head>
<title>Wastpol</title>
</Head>
<div className="flex md:flex-row flex-col justify-between px-8 mt-2">
<Nav />
<UserTop session={session} />
</div>
<main className="flex flex-1 flex-col items-center">
<div className="flex flex-row m-24 space-x-24">
<div>
1. Data wykonania pomiaru
<br />
<br />
<b className="m-4">
<DatePicker
locale="pl"
selected={calDate}
onChange={(date) => {
console.log(date);
console.log(typeof date);
const day = date.getDate().toString().padStart(2, "0"); // Add leading zero if necessary
const month = (date.getMonth() + 1)
.toString()
.padStart(2, "0"); // Month is 0-indexed, add 1 to get the correct month
const year = date.getFullYear();
const formattedDate = `${day}.${month}.${year}`;
console.log(formattedDate);
setGround((current) => ({
...current,
date: formattedDate,
}));
setCalDate(date);
}}
placeholderText="Wybierz datę"
dateFormat="dd.MM.yyyy"
/>
</b>
<br />
<br />
<RadioGroup
label="2. Wymagane uziemienie"
size={16}
value={neededValue}
options={options}
onChange={(event) => {
setNeededValue(event.target.value);
}}
/>
<TextInputField
label="3. Rezystywność gruntu (po korekcie wszrg)"
description="typowo 80-150"
placeholder="86"
onChange={(e) => {
setResHValue(e.target.value);
setResVValue((ground.wanted==30)?
getRandomInt(
parseInt(e.target.value) - 10,
parseInt(e.target.value) - 4
):getRandomInt(
parseInt(e.target.value) - 20,
parseInt(e.target.value) - 10
)
);
}}
value={resHValue}
/>
<TextInputField
label="4. Numer protokołu rezystywności gruntu"
// description=""
placeholder="435"
onChange={(e) =>
setGround((current) => ({ ...current, no: e.target.value }))
}
value={ground.no}
/>{" "}
</div>
<div>
<TextInputField
label="5. Tytuł projektu"
description="Budowa/modernizacja przyłącza/sieci kablowej/napowietrznej"
placeholder="Budowa przyłącza kablowego nN"
width={440}
onChange={(e) =>
setGround((current) => ({
...current,
pr_title: e.target.value,
}))
}
value={ground.pr_title}
/>
<TextInputField
label="6. Obiekt"
// description=""
placeholder="Przyłącz kablowy nN"
onChange={(e) =>
setGround((current) => ({
...current,
object: e.target.value,
}))
}
value={ground.object}
/>
<TextInputField
label="7. Miejscowość"
// description=""
placeholder="Paszyn"
onChange={(e) =>
setGround((current) => ({
...current,
in_city: e.target.value,
}))
}
value={ground.in_city}
/>
<TextInputField
label="8. Gmina/Obręb"
// description=""
placeholder="gm. Chełmiec lub obr. 116"
onChange={(e) =>
setGround((current) => ({
...current,
commune: e.target.value,
}))
}
value={ground.commune}
/>
<TextInputField
label="9. Działki"
// description=""
placeholder="Wszystkie numery działek"
onChange={(e) =>
setGround((current) => ({
...current,
all_parcels: e.target.value,
}))
}
value={ground.all_parcels}
/>
<TextInputField
label="10. Działka przyłączana"
// description=""
placeholder="423"
onChange={(e) =>
setGround((current) => ({
...current,
target_parcel: e.target.value,
}))
}
value={ground.target_parcel}
/>
<TextInputField
label="11. Współrzędne układu uziomowego"
// description=""
placeholder={"49°42'54\"N 20°38'0\"E"}
onChange={(e) =>
setGround((current) => ({
...current,
geo_data: e.target.value,
}))
}
value={ground.geo_data}
/>{" "}
</div>
<div>
<RadioGroup
label="1. Projektowany/istniejący"
size={16}
value={objValue1}
options={objOptions1}
onChange={(event) => {
setObjValue1(event.target.value);
setGround((current) => ({
...current,
objValue1: event.target.value,
}));
}}
/>
<TextInputField
label="2. Nazwa uziemianego obiektu"
// description=""
placeholder={"ZK2a-1P lub słup nr 54"}
width={440}
onChange={(e) =>
setGround((current) => ({
...current,
objName: e.target.value,
}))
}
value={ground.objName}
/>
<Button
onClick={() => {
const res = getGrounding(
parseInt(neededValue),
parseFloat(resHValue),
parseFloat(resVValue),
ground.date
);
setGround((current) => ({ ...current, ...res }));
}}
appearance="primary"
// className="p-2 bg-slate-200 rounded-md"
>
Oblicz Uziemienie
</Button>
<Button
onClick={() => {
const docx = generateDocument(ground);
}}
appearance="primary"
// className="p-2 bg-slate-200 rounded-md"
>
Generuj Opis
</Button>
<Button
onClick={generateDxf}
appearance="primary"
// className="p-2 bg-slate-200 rounded-md"
>
Generuj Rysunek
</Button>
<h2>
Uziemienie poziome: {ground.result_h} Ω ({ground.wszrg_h})
</h2>
<h2>
Uziemienie pionowe: {ground.result_v} Ω ({ground.wszrg_v})
</h2>
<h2>Uziemienie: {ground.result} Ω</h2>
<h2>Szpile: {ground.rod_num} szt</h2>
<h2>Bednarka: {ground.hor_len} m</h2>
</div>
</div>
</main>
</div>
);
}
return (
<div className="grid place-items-center h-screen">
<Head>
<title>Wastpol</title>
</Head>
<div className="flex flex-col justify-center">
<h2 className="p-2">Nie zalogowano</h2>
<br></br>
<Button
onClick={() => signIn()}
appearance="primary"
// className="p-2 bg-slate-200 rounded-md"
>
Zaloguj
</Button>
</div>
</div>
);
}

View File

@@ -0,0 +1,68 @@
import { useEffect, useState } from 'react';
const MapComponent = ({ latitude, longitude, name, description, officeType }) => {
const [mapLoaded, setMapLoaded] = useState(false);
useEffect(() => {
// Load Leaflet on client-side only
if (typeof window !== 'undefined' && latitude && longitude) {
// Import Leaflet dynamically
import('leaflet').then((L) => {
// Safety check to see if map element exists
const mapElement = document.getElementById('map');
if (!mapElement) return;
// Clean up any existing map instance
if (mapElement._leaflet_map) {
mapElement._leaflet_map.remove();
}
// Initialize map
const map = L.map('map').setView([latitude, longitude], 15);
// Store the map instance on the DOM element to clean up later
mapElement._leaflet_map = map;
// Add OpenStreetMap tiles
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© OpenStreetMap contributors',
maxZoom: 19
}).addTo(map);
// Add marker for post office
const marker = L.marker([latitude, longitude])
.addTo(map)
.bindPopup(`
<div style="font-family: 'Segoe UI', sans-serif;">
<h4 style="margin: 0 0 8px 0; color: #004d99;">${name}</h4>
<p style="margin: 4px 0;"><strong>📍 Adres:</strong><br>${description.street} ${description.houseNumber}<br>${description.zipCode} ${description.city}</p>
<p style="margin: 4px 0;"><strong>🏢 Typ:</strong> ${officeType === 'UP' ? 'Urząd Pocztowy' : officeType}</p>
</div>
`, {
maxWidth: 300
});
// Open popup by default
marker.openPopup();
// Make sure map renders correctly
setTimeout(() => {
map.invalidateSize();
setMapLoaded(true);
}, 300);
});
}
// Clean up function
return () => {
const mapElement = document.getElementById('map');
if (mapElement && mapElement._leaflet_map) {
mapElement._leaflet_map.remove();
}
};
}, [latitude, longitude, name, description, officeType]);
return <div id="map" style={{ height: '400px', borderRadius: '8px' }}></div>;
};
export default MapComponent;

View File

@@ -1,107 +1,239 @@
import { useState } from "react";
import {
Pane,
TextInputField,
TextareaField,
Button,
BuildIcon,
toaster,
Alert,
} from "evergreen-ui";
import { useState, useEffect } from "react";
import { useRouter } from "next/router";
import { Card, CardHeader, CardContent, CardTitle, CardDescription, Button, Input, Textarea, Alert } from "../ui/components";
import { ChartBarIcon, ArrowDownTrayIcon as DownloadIcon, EyeIcon } from '@heroicons/react/24/outline';
import axios from "axios";
import Footer from "./footer";
export default function Generator() {
const [profil, setProfil] = useState();
const [args, setArgs] = useState({ scale: 200 });
const [args, setArgs] = useState({
scale: 200,
elementOne: 0,
elementTwo: 0,
});
const [isLoading, setIsLoading] = useState(false);
const [previewVisible, setPreviewVisible] = useState(false);
const { query } = useRouter();
const ElementOneOptions = [
{ label: "Nic", value: "0" },
{ label: "Słup", value: "1" },
{ label: "Dom", value: "2" },
];
const ElementTwoOptions = [
{ label: "Nic", value: "0" },
{ label: "Słup", value: "1" },
{ label: "Dom", value: "2" },
];
useEffect(() => {
if (query.external == "tru") {
axios
.post("/api/readtext", {
id: query.id,
})
.then(function (response) {
setProfil(response.data.data);
document.getElementById("textarea-1").value = response.data.data;
console.log(response.data.data);
});
}
}, []);
const getPath = (e, path) => {
let newLines = [];
const lines = path.split("\n");
for (let line of path.split("\n")) {
if (line[0] == "P") continue;
if (line[0] == "S") continue;
if (line[0] == "X") continue;
// Parse the data more accurately
for (let line of lines) {
line = line.trim();
// Skip header lines, segment lines, and empty lines
if (line.startsWith("Próbkowanie") ||
line.startsWith("Segment") ||
line.startsWith("X:") ||
line === "") {
continue;
}
newLines.push(line);
}
let xs = [];
let ys = [];
let zs = [];
let toti = 0;
let al = 0;
for (let line of newLines) {
al += 1;
let theLine = line.split(",");
//console.log(theLine)
if (
xs &&
parseFloat(theLine[0]) == xs[-1] &&
parseFloat(theLine[1]) == ys[-1]
)
continue;
if (al > 2) {
let dist =
((xs[xs.length - 1] - xs[xs.length - 2]) ** 2 +
(ys[ys.length - 1] - ys[ys.length - 2]) ** 2) **
0.5;
toti += Math.round(dist);
}
xs.push(parseFloat(theLine[0]));
ys.push(parseFloat(theLine[1]));
zs.push(parseFloat(theLine[2]));
if (newLines.length === 0) {
console.log("No valid coordinate data found");
return;
}
let prevLine = ["0", "0", "0"];
let scaleFactor = 100000 / 2000;
const canvas = document.getElementById("canvas");
if (!canvas.getContext) {
console.log("canvas err");
toaster.warning("canvas err");
let points = [];
// Parse coordinates more reliably
for (let line of newLines) {
const coords = line.split(/[,\t]+/).map(s => s.trim());
if (coords.length >= 3) {
const x = parseFloat(coords[0]);
const y = parseFloat(coords[1]);
const z = parseFloat(coords[2]);
if (!isNaN(x) && !isNaN(y) && !isNaN(z)) {
points.push({ x, y, z });
}
}
}
if (points.length === 0) {
console.log("No valid points parsed");
return;
}
console.log(`Parsed ${points.length} points`);
const canvas = document.getElementById("canvas");
console.log("Canvas element:", canvas);
if (!canvas || !canvas.getContext) {
console.log("canvas not found or no context available");
console.log("canvas exists:", !!canvas);
console.log("getContext available:", canvas ? !!canvas.getContext : false);
return;
}
const ctx = canvas.getContext("2d");
ctx.clearRect(0, 0, 700, 700);
// set line stroke and line width
ctx.strokeStyle = "red";
ctx.lineWidth = 2;
let totalH = Math.max(...zs);
let minH = Math.min(...zs);
for (let line = 0; line < xs.length - 1; line++) {
let theLine = [xs[line], ys[line], zs[line]];
let x = parseFloat(theLine[0]) - parseFloat(prevLine[0]);
let y = parseFloat(theLine[1]) - parseFloat(prevLine[1]);
let x1 = line * scaleFactor;
let y1 = zs[line];
let x2 = (line + 1) * scaleFactor;
let y2 = zs[line + 1];
//console.log(x1, y1, x2, y2);
let b0 = 0;
let b1 = 500;
let z1 = b0 + (b1 - b0) * ((y1 - totalH) / (minH - totalH));
let z2 = b0 + (b1 - b0) * ((y2 - totalH) / (minH - totalH));
//b0 + (b1 - b0) * ((x1-0)/(toti*scaleFactor-0));
let x12 = b0 + (b1 - b0) * ((x1 - 0) / (toti * scaleFactor));
let x22 = b0 + (b1 - b0) * ((x2 - 0) / (toti * scaleFactor));
//console.log(x12);
const canvasWidth = 800; // Match the canvas width
const canvasHeight = 400; // Match the canvas height
const padding = 50;
// Clear canvas
ctx.clearRect(0, 0, canvasWidth, canvasHeight);
// Calculate bounds
const xCoords = points.map(p => p.x);
const yCoords = points.map(p => p.y);
const zCoords = points.map(p => p.z);
const minX = Math.min(...xCoords);
const maxX = Math.max(...xCoords);
const minY = Math.min(...yCoords);
const maxY = Math.max(...yCoords);
const minZ = Math.min(...zCoords);
const maxZ = Math.max(...zCoords);
// Calculate distances along the path
let distances = [0];
let totalDistance = 0;
for (let i = 1; i < points.length; i++) {
const dx = points[i].x - points[i-1].x;
const dy = points[i].y - points[i-1].y;
const distance = Math.sqrt(dx * dx + dy * dy);
totalDistance += distance;
distances.push(totalDistance);
}
console.log(`Total distance: ${totalDistance.toFixed(2)}m, Height range: ${minZ.toFixed(2)}m - ${maxZ.toFixed(2)}m`);
// Set up drawing area
const drawWidth = canvasWidth - 2 * padding;
const drawHeight = canvasHeight - 2 * padding;
// Draw coordinate system
ctx.strokeStyle = "#E5E7EB";
ctx.lineWidth = 1;
// Draw grid
const gridLines = 10;
for (let i = 0; i <= gridLines; i++) {
const x = padding + (i / gridLines) * drawWidth;
const y = padding + (i / gridLines) * drawHeight;
// Vertical grid lines
ctx.beginPath();
ctx.moveTo(x12, z1);
ctx.lineTo(x22, z2);
ctx.moveTo(x, padding);
ctx.lineTo(x, canvasHeight - padding);
ctx.stroke();
// Horizontal grid lines
ctx.beginPath();
ctx.moveTo(padding, y);
ctx.lineTo(canvasWidth - padding, y);
ctx.stroke();
}
};
const parsePreview = (e) => {
// console.log(dxf);
// Draw axes
ctx.strokeStyle = "#6B7280";
ctx.lineWidth = 2;
// X-axis (bottom)
ctx.beginPath();
ctx.moveTo(padding, canvasHeight - padding);
ctx.lineTo(canvasWidth - padding, canvasHeight - padding);
ctx.stroke();
// Y-axis (left)
ctx.beginPath();
ctx.moveTo(padding, padding);
ctx.lineTo(padding, canvasHeight - padding);
ctx.stroke();
// Draw labels
ctx.fillStyle = "#374151";
ctx.font = "12px sans-serif";
ctx.textAlign = "center";
// Distance labels (bottom)
for (let i = 0; i <= 5; i++) {
const distance = (totalDistance * i) / 5;
const x = padding + (i / 5) * drawWidth;
ctx.fillText(`${distance.toFixed(0)}m`, x, canvasHeight - padding + 20);
}
// Height labels (left)
ctx.textAlign = "right";
for (let i = 0; i <= 5; i++) {
const height = minZ + ((maxZ - minZ) * i) / 5;
const y = canvasHeight - padding - (i / 5) * drawHeight;
ctx.fillText(`${height.toFixed(1)}m`, padding - 10, y + 4);
}
// Draw profile line
ctx.strokeStyle = "#3B82F6";
ctx.lineWidth = 3;
ctx.lineCap = "round";
ctx.lineJoin = "round";
ctx.beginPath();
for (let i = 0; i < points.length; i++) {
const x = padding + (distances[i] / totalDistance) * drawWidth;
const y = canvasHeight - padding - ((points[i].z - minZ) / (maxZ - minZ)) * drawHeight;
if (i === 0) {
ctx.moveTo(x, y);
} else {
ctx.lineTo(x, y);
}
}
ctx.stroke();
// Draw elevation points
ctx.fillStyle = "#3B82F6";
for (let i = 0; i < points.length; i++) {
const x = padding + (distances[i] / totalDistance) * drawWidth;
const y = canvasHeight - padding - ((points[i].z - minZ) / (maxZ - minZ)) * drawHeight;
ctx.beginPath();
ctx.arc(x, y, 4, 0, 2 * Math.PI);
ctx.fill();
}
// Add title
ctx.fillStyle = "#1F2937";
ctx.font = "16px sans-serif";
ctx.textAlign = "center";
ctx.fillText("Profil terenu", canvasWidth / 2, 30);
setPreviewVisible(true);
};
const py = (e, profil, args) => {
e.preventDefault();
setIsLoading(true);
axios
.post("/api/spawn", {
@@ -109,75 +241,205 @@ export default function Generator() {
arguments: args,
})
.then(function (response) {
setIsLoading(false);
console.log(response);
if (response.data.toString().startsWith("Py Error")) {
toaster.danger(response.data);
alert("Błąd: " + response.data);
return;
}
toaster.warning(response.data.data);
document.getElementById("down").download =
response.data.filename.toString() + ".dxf";
document.getElementById("down").download = response.data.filename.toString() + ".dxf";
document.getElementById("down").click();
})
.catch(function (error) {
setIsLoading(false);
console.log(error);
});
};
return (
<div className="flex xl:flex-row flex-col">
<div className="flex flex-col">
<Pane width={480}>
<TextareaField
id="textarea-1"
label="Dane z Geoportalu:"
placeholder="Próbkowanie: 1 ..."
onChange={(e) => {
//console.log(e.target.value);
setProfil(e.target.value);
parsePreview(e);
getPath(e, e.target.value);
}}
/>
<div className="grid grid-cols-1 xl:grid-cols-2 gap-8">
{/* Input Section */}
<div className="space-y-6">
<Card>
<CardHeader>
<CardTitle className="flex items-center space-x-2">
<ChartBarIcon className="w-5 h-5 text-blue-600" />
<span>Dane wejściowe</span>
</CardTitle>
<CardDescription>
Wklej dane z Geoportalu lub wprowadź własne współrzędne
</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<Textarea
id="textarea-1"
label="Dane z Geoportalu"
placeholder="Próbkowanie: 1 ..."
rows={8}
onChange={(e) => {
setProfil(e.target.value);
}}
className="font-mono text-sm"
/>
<TextInputField
label="Skala:"
placeholder="200"
width={100}
onChange={(e) => {
console.log(e.target.value);
setArgs({ ...args, scale: e.target.value });
}}
/>
<Button
marginY={8}
marginRight={12}
appearance="default"
iconAfter={BuildIcon}
onClick={(e) => {
if (document.getElementById("textarea-1").value == "") {
toaster.danger("Pole danych nie może być puste");
} else if (
!document
.getElementById("textarea-1")
.value.startsWith("Próbkowanie")
) {
toaster.danger("Błędne dane");
} else {
py(e, profil, args);
}
}}
>
Generuj
</Button>
</Pane>
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
<Input
label="Skala"
placeholder="200"
type="number"
value={args.scale}
onChange={(e) => setArgs({ ...args, scale: e.target.value })}
/>
<a href="test.dxf" download="test.dxf" id="down">
{" "}
</a>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
Element lewy
</label>
<select
className="block w-full rounded-lg border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500"
value={args.elementOne}
onChange={(e) => setArgs({ ...args, elementOne: e.target.value })}
>
{ElementOneOptions.map(option => (
<option key={option.value} value={option.value}>
{option.label}
</option>
))}
</select>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
Element prawy
</label>
<select
className="block w-full rounded-lg border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500"
value={args.elementTwo}
onChange={(e) => setArgs({ ...args, elementTwo: e.target.value })}
>
{ElementTwoOptions.map(option => (
<option key={option.value} value={option.value}>
{option.label}
</option>
))}
</select>
</div>
</div>
<div className="flex space-x-3">
<Button
onClick={() => {
const textareaValue = document.getElementById("textarea-1").value;
if (textareaValue && textareaValue.startsWith("Próbkowanie")) {
// First set preview visible, then draw after a small delay
setPreviewVisible(true);
setTimeout(() => {
getPath(null, textareaValue);
}, 50);
} else {
alert("Wprowadź poprawne dane z Geoportalu");
}
}}
variant="outline"
className="flex-1"
>
<EyeIcon className="w-5 h-5 mr-2" />
Podgląd
</Button>
<Button
onClick={(e) => {
const textareaValue = document.getElementById("textarea-1").value;
if (textareaValue === "") {
alert("Pole danych nie może być puste");
} else if (!textareaValue.startsWith("Próbkowanie")) {
alert("Błędne dane");
} else {
py(e, profil, args);
}
}}
disabled={isLoading}
loading={isLoading}
className="flex-1"
>
<DownloadIcon className="w-5 h-5 mr-2" />
{isLoading ? 'Generowanie...' : 'Generuj profil'}
</Button>
</div>
</CardContent>
</Card>
</div>
<Pane className="ml-8 shadow-md rounded-md">
<canvas id="canvas" height="500" width="700"></canvas>
</Pane>
{/* Preview Section */}
<div>
<Card>
<CardHeader>
<CardTitle className="flex items-center space-x-2">
<EyeIcon className="w-5 h-5 text-green-600" />
<span>Podgląd profilu</span>
</CardTitle>
<CardDescription>
Wizualizacja profilu terenu na podstawie wprowadzonych danych
</CardDescription>
</CardHeader>
<CardContent>
<div className="bg-gray-50 rounded-lg p-4">
<canvas
id="canvas"
height="400"
width="800"
className="border border-gray-200 rounded bg-white w-full max-w-full"
style={{ display: previewVisible ? 'block' : 'none' }}
/>
{!previewVisible && (
<div className="bg-gray-50 rounded-lg p-8 text-center">
<div className="mx-auto w-16 h-16 bg-gray-200 rounded-full flex items-center justify-center mb-4">
<ChartBarIcon className="w-8 h-8 text-gray-400" />
</div>
<p className="text-gray-500">Wprowadź dane i kliknij "Podgląd" aby zobaczyć profil</p>
</div>
)}
</div>
</CardContent>
</Card>
{/* Instructions */}
<Card className="mt-6">
<CardHeader>
<CardTitle>Instrukcje</CardTitle>
</CardHeader>
<CardContent>
<div className="space-y-3 text-sm">
<div className="flex items-start space-x-3">
<div className="flex-shrink-0 w-6 h-6 bg-blue-100 rounded-full flex items-center justify-center">
<span className="text-xs font-semibold text-blue-600">1</span>
</div>
<p className="text-gray-600">Skopiuj dane z Geoportalu w formacie "Próbkowanie: 1"</p>
</div>
<div className="flex items-start space-x-3">
<div className="flex-shrink-0 w-6 h-6 bg-blue-100 rounded-full flex items-center justify-center">
<span className="text-xs font-semibold text-blue-600">2</span>
</div>
<p className="text-gray-600">Ustaw skalę i wybierz elementy dodatkowe</p>
</div>
<div className="flex items-start space-x-3">
<div className="flex-shrink-0 w-6 h-6 bg-blue-100 rounded-full flex items-center justify-center">
<span className="text-xs font-semibold text-blue-600">3</span>
</div>
<p className="text-gray-600">Kliknij "Podgląd" aby zobaczyć wizualizację profilu</p>
</div>
<div className="flex items-start space-x-3">
<div className="flex-shrink-0 w-6 h-6 bg-blue-100 rounded-full flex items-center justify-center">
<span className="text-xs font-semibold text-blue-600">4</span>
</div>
<p className="text-gray-600">Kliknij "Generuj profil" aby pobrać plik DXF</p>
</div>
</div>
</CardContent>
</Card>
</div>
<a href="test.dxf" download="test.dxf" id="down" className="hidden" />
</div>
);
}

View File

@@ -0,0 +1,294 @@
import { useState, useEffect } from "react";
import { useRouter } from "next/router";
import { Card, CardHeader, CardContent, CardTitle, CardDescription, Button, Input, Textarea, Alert } from "../ui/components";
import { ChartBarIcon, ArrowDownTrayIcon as DownloadIcon, EyeIcon } from '@heroicons/react/24/outline';
import axios from "axios";
export default function Generator() {
const [profil, setProfil] = useState();
const [args, setArgs] = useState({
scale: 200,
elementOne: 0,
elementTwo: 0,
});
const [isLoading, setIsLoading] = useState(false);
const [previewVisible, setPreviewVisible] = useState(false);
const { query } = useRouter();
const ElementOneOptions = [
{ label: "Nic", value: "0" },
{ label: "Słup", value: "1" },
{ label: "Dom", value: "2" },
];
const ElementTwoOptions = [
{ label: "Nic", value: "0" },
{ label: "Słup", value: "1" },
{ label: "Dom", value: "2" },
];
useEffect(() => {
if (query.external == "tru") {
axios
.post("/api/readtext", {
id: query.id,
})
.then(function (response) {
setProfil(response.data.data);
document.getElementById("textarea-1").value = response.data.data;
console.log(response.data.data);
});
}
}, []);
const getPath = (e, path) => {
let newLines = [];
for (let line of path.split("\n")) {
if (line[0] == "P") continue;
if (line[0] == "S") continue;
if (line[0] == "X") continue;
newLines.push(line);
}
let xs = [], ys = [], zs = [];
let toti = 0, al = 0;
for (let line of newLines) {
al += 1;
let theLine = line.split(",");
if (xs && parseFloat(theLine[0]) == xs[-1] && parseFloat(theLine[1]) == ys[-1])
continue;
if (al > 2) {
let dist = ((xs[xs.length - 1] - xs[xs.length - 2]) ** 2 +
(ys[ys.length - 1] - ys[ys.length - 2]) ** 2) ** 0.5;
toti += Math.round(dist);
}
xs.push(parseFloat(theLine[0]));
ys.push(parseFloat(theLine[1]));
zs.push(parseFloat(theLine[2]));
}
const canvas = document.getElementById("canvas");
if (!canvas.getContext) {
console.log("canvas err");
return;
}
const ctx = canvas.getContext("2d");
ctx.clearRect(0, 0, 700, 500);
ctx.strokeStyle = "#3B82F6";
ctx.lineWidth = 2;
let scaleFactor = 100000 / 2000;
let totalH = Math.max(...zs);
let minH = Math.min(...zs);
for (let line = 0; line < xs.length - 1; line++) {
let x1 = line * scaleFactor;
let y1 = zs[line];
let x2 = (line + 1) * scaleFactor;
let y2 = zs[line + 1];
let b0 = 50, b1 = 450;
let z1 = b0 + (b1 - b0) * ((y1 - totalH) / (minH - totalH));
let z2 = b0 + (b1 - b0) * ((y2 - totalH) / (minH - totalH));
let x12 = b0 + (b1 - b0) * ((x1 - 0) / (toti * scaleFactor));
let x22 = b0 + (b1 - b0) * ((x2 - 0) / (toti * scaleFactor));
ctx.beginPath();
ctx.moveTo(x12, z1);
ctx.lineTo(x22, z2);
ctx.stroke();
}
setPreviewVisible(true);
};
const py = (e, profil, args) => {
e.preventDefault();
setIsLoading(true);
axios
.post("/api/spawn", {
profil: profil,
arguments: args,
})
.then(function (response) {
setIsLoading(false);
console.log(response);
if (response.data.toString().startsWith("Py Error")) {
alert("Błąd: " + response.data);
return;
}
document.getElementById("down").download = response.data.filename.toString() + ".dxf";
document.getElementById("down").click();
})
.catch(function (error) {
setIsLoading(false);
console.log(error);
});
};
return (
<div className="grid grid-cols-1 xl:grid-cols-2 gap-8">
{/* Input Section */}
<div className="space-y-6">
<Card>
<CardHeader>
<CardTitle className="flex items-center space-x-2">
<ChartBarIcon className="w-5 h-5 text-blue-600" />
<span>Dane wejściowe</span>
</CardTitle>
<CardDescription>
Wklej dane z Geoportalu lub wprowadź własne współrzędne
</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<Textarea
id="textarea-1"
label="Dane z Geoportalu"
placeholder="Próbkowanie: 1 ..."
rows={8}
onChange={(e) => {
setProfil(e.target.value);
getPath(e, e.target.value);
}}
className="font-mono text-sm"
/>
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
<Input
label="Skala"
placeholder="200"
type="number"
value={args.scale}
onChange={(e) => setArgs({ ...args, scale: e.target.value })}
/>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
Element lewy
</label>
<select
className="block w-full rounded-lg border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500"
value={args.elementOne}
onChange={(e) => setArgs({ ...args, elementOne: e.target.value })}
>
{ElementOneOptions.map(option => (
<option key={option.value} value={option.value}>
{option.label}
</option>
))}
</select>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
Element prawy
</label>
<select
className="block w-full rounded-lg border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500"
value={args.elementTwo}
onChange={(e) => setArgs({ ...args, elementTwo: e.target.value })}
>
{ElementTwoOptions.map(option => (
<option key={option.value} value={option.value}>
{option.label}
</option>
))}
</select>
</div>
</div>
<Button
onClick={(e) => {
const textareaValue = document.getElementById("textarea-1").value;
if (textareaValue === "") {
alert("Pole danych nie może być puste");
} else if (!textareaValue.startsWith("Próbkowanie")) {
alert("Błędne dane");
} else {
py(e, profil, args);
}
}}
disabled={isLoading}
loading={isLoading}
className="w-full"
>
<DownloadIcon className="w-5 h-5 mr-2" />
{isLoading ? 'Generowanie...' : 'Generuj profil'}
</Button>
</CardContent>
</Card>
</div>
{/* Preview Section */}
<div>
<Card>
<CardHeader>
<CardTitle className="flex items-center space-x-2">
<EyeIcon className="w-5 h-5 text-green-600" />
<span>Podgląd profilu</span>
</CardTitle>
<CardDescription>
Wizualizacja profilu terenu na podstawie wprowadzonych danych
</CardDescription>
</CardHeader>
<CardContent>
{previewVisible ? (
<div className="bg-gray-50 rounded-lg p-4">
<canvas
id="canvas"
height="500"
width="700"
className="border border-gray-200 rounded bg-white"
/>
</div>
) : (
<div className="bg-gray-50 rounded-lg p-8 text-center">
<div className="mx-auto w-16 h-16 bg-gray-200 rounded-full flex items-center justify-center mb-4">
<ChartBarIcon className="w-8 h-8 text-gray-400" />
</div>
<p className="text-gray-500">Wprowadź dane aby zobaczyć podgląd profilu</p>
</div>
)}
</CardContent>
</Card>
{/* Instructions */}
<Card className="mt-6">
<CardHeader>
<CardTitle>Instrukcje</CardTitle>
</CardHeader>
<CardContent>
<div className="space-y-3 text-sm">
<div className="flex items-start space-x-3">
<div className="flex-shrink-0 w-6 h-6 bg-blue-100 rounded-full flex items-center justify-center">
<span className="text-xs font-semibold text-blue-600">1</span>
</div>
<p className="text-gray-600">Skopiuj dane z Geoportalu w formacie "Próbkowanie: 1"</p>
</div>
<div className="flex items-start space-x-3">
<div className="flex-shrink-0 w-6 h-6 bg-blue-100 rounded-full flex items-center justify-center">
<span className="text-xs font-semibold text-blue-600">2</span>
</div>
<p className="text-gray-600">Ustaw skalę i wybierz elementy dodatkowe</p>
</div>
<div className="flex items-start space-x-3">
<div className="flex-shrink-0 w-6 h-6 bg-blue-100 rounded-full flex items-center justify-center">
<span className="text-xs font-semibold text-blue-600">3</span>
</div>
<p className="text-gray-600">Kliknij "Generuj profil" aby pobrać plik DXF</p>
</div>
</div>
</CardContent>
</Card>
</div>
<a href="test.dxf" download="test.dxf" id="down" className="hidden" />
</div>
);
}

View File

@@ -1,25 +1,12 @@
import { useState, useEffect } from "react";
import {
Pane,
TextInputField,
TextareaField,
Button,
toaster,
Alert,
TrashIcon,
Icon,
} from "evergreen-ui";
import { useState } from "react";
import { Card, CardHeader, CardContent, CardTitle, CardDescription, Button, Input } from "../ui/components";
import { PencilIcon, PlusIcon, TrashIcon, ArrowDownTrayIcon as DownloadIcon } from '@heroicons/react/24/outline';
import axios from "axios";
import Footer from "./footer";
import Generator from "./generator";
export default function Manual() {
const [points, setPoints] = useState([{ id: 0, len: 0, height: 0 }]);
const [args, setArgs] = useState({ scale: 200 });
//useEffect(() => {
//do something here
// }, [points]);
const [isLoading, setIsLoading] = useState(false);
const reIndex = () => {
let newId = 0;
@@ -32,7 +19,32 @@ export default function Manual() {
setPoints([...newPoints]);
};
const addPoint = () => {
setPoints([
...points,
{
id: points.length,
len: Number(points[points.length - 1].len) + 1,
height: 0,
},
]);
};
const removePoint = (index) => {
let newPoints = points;
newPoints.splice(index, 1);
setPoints([...newPoints]);
reIndex();
};
const updatePoint = (index, field, value) => {
let newPoints = points;
newPoints[index][field] = value;
setPoints([...newPoints]);
};
const generation = (e) => {
setIsLoading(true);
let pointString = "Próbkowanie: 1\nSegment 0: w dół\nX: Y, Z";
for (let point of points) {
@@ -42,7 +54,6 @@ export default function Manual() {
pointString += point.height;
}
console.log(pointString);
py(e, pointString, args);
};
@@ -55,103 +66,176 @@ export default function Manual() {
arguments: args,
})
.then(function (response) {
setIsLoading(false);
console.log(response);
if (response.data.toString().startsWith("Py Error")) {
toaster.danger(response.data);
alert("Błąd: " + response.data);
return;
}
toaster.warning(response.data.data);
document.getElementById("down").download =
response.data.filename.toString() + ".dxf";
document.getElementById("down").download = response.data.filename.toString() + ".dxf";
document.getElementById("down").click();
})
.catch(function (error) {
setIsLoading(false);
console.log(error);
});
};
return (
<div className="flex xl:flex-row flex-col">
<div className="flex flex-col">
<Pane width={480}>
{points.map((point, index) => (
<Pane className="flex flex-row items-center">
<TextInputField
id={`len-${index}`}
placeholder="0 m"
onChange={(e) => {
console.log(e.target.value);
let newPoints = points;
newPoints[index].len = e.target.value;
setPoints([...newPoints]);
}}
value={point.len}
></TextInputField>
<TextInputField
id={`h-${index}`}
placeholder="0 m n.p.m."
onChange={(e) => {
console.log(e.target.value);
let newPoints = points;
newPoints[index].height = e.target.value;
setPoints([...newPoints]);
}}
value={point.height}
onKeyDown={(e) => {
if (e.key == "Enter") {
setPoints([
...points,
{
id: points.length,
len: Number(points[points.length - 1].len) + 1,
height: 0,
},
]);
<div className="grid grid-cols-1 xl:grid-cols-2 gap-8">
{/* Input Section */}
<div className="space-y-6">
<Card>
<CardHeader>
<CardTitle className="flex items-center space-x-2">
<PencilIcon className="w-5 h-5 text-blue-600" />
<span>Punkty profilu</span>
</CardTitle>
<CardDescription>
Wprowadź punkty profilu ręcznie - długość i wysokość nad poziomem morza
</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
{/* Scale Input */}
<Input
label="Skala"
type="number"
placeholder="200"
value={args.scale}
onChange={(e) => setArgs({ ...args, scale: e.target.value })}
/>
let nextDiv = e.target.parentNode.parentNode.nextSibling;
nextDiv.childNodes[1].childNodes[1].focus()
nextDiv.childNodes[1].childNodes[1].setSelectionRange(0, 1)
}
}}
></TextInputField>
{/* Points List */}
<div className="space-y-3 max-h-96 overflow-y-auto">
{points.map((point, index) => (
<div key={point.id} className="grid grid-cols-2 gap-3 p-3 bg-gray-50 rounded-lg">
<Input
label={`Punkt ${index + 1} - Długość (m)`}
type="number"
placeholder="0"
value={point.len}
onChange={(e) => updatePoint(index, 'len', e.target.value)}
/>
<div className="flex space-x-2">
<Input
label="Wysokość (m n.p.m.)"
type="number"
placeholder="0"
value={point.height}
onChange={(e) => updatePoint(index, 'height', e.target.value)}
onKeyDown={(e) => {
if (e.key === "Enter") {
addPoint();
// Focus next input after render
setTimeout(() => {
const nextInput = e.target.parentNode.parentNode.parentNode.nextSibling?.querySelector('input');
if (nextInput) nextInput.focus();
}, 100);
}
}}
/>
{points.length > 1 && (
<button
onClick={() => removePoint(index)}
className="self-end p-2 text-red-600 hover:bg-red-50 rounded-lg transition-colors"
title="Usuń punkt"
>
<TrashIcon className="w-5 h-5" />
</button>
)}
</div>
</div>
))}
</div>
{/* Action Buttons */}
<div className="flex space-x-3">
<Button
className="mb-5"
appearance="minimal"
onClick={() => {
let newPoints = points;
newPoints.splice(index, 1);
setPoints([...newPoints]);
reIndex();
}}
onClick={addPoint}
variant="outline"
className="flex-1"
>
<Icon icon={TrashIcon} color="danger"></Icon>
<PlusIcon className="w-5 h-5 mr-2" />
Dodaj punkt
</Button>
</Pane>
))}
</Pane>
<Button
onClick={() => {
setPoints([
...points,
{
id: points.length,
len: Number(points[points.length - 1].len) + 1,
height: 0,
},
]);
}}
>
Dodaj
</Button>
<Button
appearance="primary"
onClick={(e) => {
generation(e);
}}
>
Generuj
</Button>
<Button
onClick={generation}
disabled={isLoading}
loading={isLoading}
className="flex-1"
>
<DownloadIcon className="w-5 h-5 mr-2" />
{isLoading ? 'Generowanie...' : 'Generuj profil'}
</Button>
</div>
</CardContent>
</Card>
</div>
{/* Preview/Instructions Section */}
<div className="space-y-6">
<Card>
<CardHeader>
<CardTitle>Podgląd punktów</CardTitle>
<CardDescription>
Lista wprowadzonych punktów profilu
</CardDescription>
</CardHeader>
<CardContent>
{points.length > 0 ? (
<div className="space-y-2">
{points.map((point, index) => (
<div key={point.id} className="flex justify-between items-center p-3 bg-gray-50 rounded-lg">
<span className="font-medium text-gray-900">Punkt {index + 1}</span>
<div className="text-sm text-gray-600">
<span className="mr-4">Długość: {point.len}m</span>
<span>Wysokość: {point.height}m</span>
</div>
</div>
))}
</div>
) : (
<div className="text-center py-8">
<div className="mx-auto w-16 h-16 bg-gray-100 rounded-full flex items-center justify-center mb-4">
<PencilIcon className="w-8 h-8 text-gray-400" />
</div>
<p className="text-gray-500">Brak punktów do wyświetlenia</p>
</div>
)}
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>Instrukcje</CardTitle>
</CardHeader>
<CardContent>
<div className="space-y-3 text-sm">
<div className="flex items-start space-x-3">
<div className="flex-shrink-0 w-6 h-6 bg-blue-100 rounded-full flex items-center justify-center">
<span className="text-xs font-semibold text-blue-600">1</span>
</div>
<p className="text-gray-600">Wprowadź kolejne punkty profilu z długością i wysokością</p>
</div>
<div className="flex items-start space-x-3">
<div className="flex-shrink-0 w-6 h-6 bg-blue-100 rounded-full flex items-center justify-center">
<span className="text-xs font-semibold text-blue-600">2</span>
</div>
<p className="text-gray-600">Użyj klawisza Enter aby szybko dodać następny punkt</p>
</div>
<div className="flex items-start space-x-3">
<div className="flex-shrink-0 w-6 h-6 bg-blue-100 rounded-full flex items-center justify-center">
<span className="text-xs font-semibold text-blue-600">3</span>
</div>
<p className="text-gray-600">Ustaw skalę i kliknij "Generuj profil"</p>
</div>
</div>
</CardContent>
</Card>
</div>
<a href="test.dxf" download="test.dxf" id="down" className="hidden" />
</div>
);
}

View File

@@ -0,0 +1,241 @@
import { useState } from "react";
import { Card, CardHeader, CardContent, CardTitle, CardDescription, Button, Input } from "../ui/components";
import { PencilIcon, PlusIcon, TrashIcon, ArrowDownTrayIcon as DownloadIcon } from '@heroicons/react/24/outline';
import axios from "axios";
export default function Manual() {
const [points, setPoints] = useState([{ id: 0, len: 0, height: 0 }]);
const [args, setArgs] = useState({ scale: 200 });
const [isLoading, setIsLoading] = useState(false);
const reIndex = () => {
let newId = 0;
let newPoints = [];
for (let point of points) {
point.id = newId;
newPoints.push(point);
newId += 1;
}
setPoints([...newPoints]);
};
const addPoint = () => {
setPoints([
...points,
{
id: points.length,
len: Number(points[points.length - 1].len) + 1,
height: 0,
},
]);
};
const removePoint = (index) => {
let newPoints = points;
newPoints.splice(index, 1);
setPoints([...newPoints]);
reIndex();
};
const updatePoint = (index, field, value) => {
let newPoints = points;
newPoints[index][field] = value;
setPoints([...newPoints]);
};
const generation = (e) => {
setIsLoading(true);
let pointString = "Próbkowanie: 1\nSegment 0: w dół\nX: Y, Z";
for (let point of points) {
pointString += "\n";
pointString += Number(point.len) * 1.0;
pointString += ", 1.0, ";
pointString += point.height;
}
py(e, pointString, args);
};
const py = (e, profil, args) => {
e.preventDefault();
axios
.post("/api/spawn", {
profil: profil,
arguments: args,
})
.then(function (response) {
setIsLoading(false);
console.log(response);
if (response.data.toString().startsWith("Py Error")) {
alert("Błąd: " + response.data);
return;
}
document.getElementById("down").download = response.data.filename.toString() + ".dxf";
document.getElementById("down").click();
})
.catch(function (error) {
setIsLoading(false);
console.log(error);
});
};
return (
<div className="grid grid-cols-1 xl:grid-cols-2 gap-8">
{/* Input Section */}
<div className="space-y-6">
<Card>
<CardHeader>
<CardTitle className="flex items-center space-x-2">
<PencilIcon className="w-5 h-5 text-blue-600" />
<span>Punkty profilu</span>
</CardTitle>
<CardDescription>
Wprowadź punkty profilu ręcznie - długość i wysokość nad poziomem morza
</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
{/* Scale Input */}
<Input
label="Skala"
type="number"
placeholder="200"
value={args.scale}
onChange={(e) => setArgs({ ...args, scale: e.target.value })}
/>
{/* Points List */}
<div className="space-y-3 max-h-96 overflow-y-auto">
{points.map((point, index) => (
<div key={point.id} className="grid grid-cols-2 gap-3 p-3 bg-gray-50 rounded-lg">
<Input
label={`Punkt ${index + 1} - Długość (m)`}
type="number"
placeholder="0"
value={point.len}
onChange={(e) => updatePoint(index, 'len', e.target.value)}
/>
<div className="flex space-x-2">
<Input
label="Wysokość (m n.p.m.)"
type="number"
placeholder="0"
value={point.height}
onChange={(e) => updatePoint(index, 'height', e.target.value)}
onKeyDown={(e) => {
if (e.key === "Enter") {
addPoint();
// Focus next input after render
setTimeout(() => {
const nextInput = e.target.parentNode.parentNode.parentNode.nextSibling?.querySelector('input');
if (nextInput) nextInput.focus();
}, 100);
}
}}
/>
{points.length > 1 && (
<button
onClick={() => removePoint(index)}
className="self-end p-2 text-red-600 hover:bg-red-50 rounded-lg transition-colors"
title="Usuń punkt"
>
<TrashIcon className="w-5 h-5" />
</button>
)}
</div>
</div>
))}
</div>
{/* Action Buttons */}
<div className="flex space-x-3">
<Button
onClick={addPoint}
variant="outline"
className="flex-1"
>
<PlusIcon className="w-5 h-5 mr-2" />
Dodaj punkt
</Button>
<Button
onClick={generation}
disabled={isLoading}
loading={isLoading}
className="flex-1"
>
<DownloadIcon className="w-5 h-5 mr-2" />
{isLoading ? 'Generowanie...' : 'Generuj profil'}
</Button>
</div>
</CardContent>
</Card>
</div>
{/* Preview/Instructions Section */}
<div className="space-y-6">
<Card>
<CardHeader>
<CardTitle>Podgląd punktów</CardTitle>
<CardDescription>
Lista wprowadzonych punktów profilu
</CardDescription>
</CardHeader>
<CardContent>
{points.length > 0 ? (
<div className="space-y-2">
{points.map((point, index) => (
<div key={point.id} className="flex justify-between items-center p-3 bg-gray-50 rounded-lg">
<span className="font-medium text-gray-900">Punkt {index + 1}</span>
<div className="text-sm text-gray-600">
<span className="mr-4">Długość: {point.len}m</span>
<span>Wysokość: {point.height}m</span>
</div>
</div>
))}
</div>
) : (
<div className="text-center py-8">
<div className="mx-auto w-16 h-16 bg-gray-100 rounded-full flex items-center justify-center mb-4">
<PencilIcon className="w-8 h-8 text-gray-400" />
</div>
<p className="text-gray-500">Brak punktów do wyświetlenia</p>
</div>
)}
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>Instrukcje</CardTitle>
</CardHeader>
<CardContent>
<div className="space-y-3 text-sm">
<div className="flex items-start space-x-3">
<div className="flex-shrink-0 w-6 h-6 bg-blue-100 rounded-full flex items-center justify-center">
<span className="text-xs font-semibold text-blue-600">1</span>
</div>
<p className="text-gray-600">Wprowadź kolejne punkty profilu z długością i wysokością</p>
</div>
<div className="flex items-start space-x-3">
<div className="flex-shrink-0 w-6 h-6 bg-blue-100 rounded-full flex items-center justify-center">
<span className="text-xs font-semibold text-blue-600">2</span>
</div>
<p className="text-gray-600">Użyj klawisza Enter aby szybko dodać następny punkt</p>
</div>
<div className="flex items-start space-x-3">
<div className="flex-shrink-0 w-6 h-6 bg-blue-100 rounded-full flex items-center justify-center">
<span className="text-xs font-semibold text-blue-600">3</span>
</div>
<p className="text-gray-600">Ustaw skalę i kliknij "Generuj profil"</p>
</div>
</div>
</CardContent>
</Card>
</div>
<a href="test.dxf" download="test.dxf" id="down" className="hidden" />
</div>
);
}

View File

@@ -1,7 +1,13 @@
import Link from "next/link";
import { useRouter } from 'next/router';
import { Pane, Heading } from "evergreen-ui";
export default function Nav() {
const router = useRouter();
const isActive = (href) => {
return router.pathname === href ? `active font-bold bg-slate-200 rounded-md` : '';
};
return (
<Pane display="flex">
<Link href="/">
@@ -9,12 +15,18 @@ export default function Nav() {
<img src="logo.png" className="h-12" />
</h2>
</Link>
{/*<Link href="/onas">
<h3 className="px-3 py-2 place-self-end">O nas</h3>
<Link href="/">
<h3 className={"px-3 py-2 place-self-end "+isActive('/')} style={{cursor: 'pointer'}}>Przekrój</h3>
</Link>
<Link href="/kontakt">
<Link href="/cross">
<h3 className={"px-3 py-2 place-self-end "+isActive('/cross')} style={{cursor: 'pointer'}}>Siatka</h3>
</Link>
<Link href="/uziomy">
<h3 className={"px-3 py-2 place-self-end "+isActive('/uziomy')} style={{cursor: 'pointer'}}>Uziomy</h3>
</Link>
{/* <Link href="/kontakt">
<h3 className="px-3 py-2 place-self-end">Kontakt</h3>
</Link>*/}
</Link> */}
</Pane>
);
}

158
components/ui/Layout.js Normal file
View File

@@ -0,0 +1,158 @@
import { useState } from 'react';
import Head from 'next/head';
import Link from 'next/link';
import { useRouter } from 'next/router';
import { useSession, signOut } from 'next-auth/react';
import {
HomeIcon,
Squares2X2Icon as GridIcon,
BoltIcon as LightningBoltIcon,
Bars3Icon as MenuIcon,
XMarkIcon as XIcon,
UserIcon,
ArrowRightOnRectangleIcon as LogoutIcon,
TruckIcon,
CpuChipIcon
} from '@heroicons/react/24/outline';
const navigationItems = [
{ name: 'Przekrój terenu', href: '/', icon: HomeIcon },
{ name: 'Siatka', href: '/cross', icon: GridIcon },
{ name: 'Uziomy', href: '/uziomy', icon: LightningBoltIcon },
{ name: 'Spadki napięcia', href: '/spadki', icon: CpuChipIcon },
{ name: 'Śledzenie przesyłek', href: '/tracking', icon: TruckIcon },
];
export default function Layout({ children, title = 'Wastpol' }) {
const [sidebarOpen, setSidebarOpen] = useState(false);
const { data: session } = useSession();
const router = useRouter();
return (
<div className="min-h-screen bg-gradient-to-br from-slate-50 to-blue-50">
<Head>
<title>{title}</title>
<meta name="description" content="Professional electrical engineering tools" />
<link rel="icon" href="/icon.png" />
</Head>
{/* Mobile sidebar */}
<div className={`fixed inset-0 z-50 lg:hidden ${sidebarOpen ? 'block' : 'hidden'}`}>
<div className="fixed inset-0 bg-gray-600 bg-opacity-75" onClick={() => setSidebarOpen(false)} />
<div className="fixed inset-y-0 left-0 flex w-64 flex-col bg-white shadow-xl">
<div className="flex h-16 items-center justify-between px-4 border-b border-gray-200">
<img className="h-8 w-auto" src="/logo.png" alt="Wastpol" />
<button
onClick={() => setSidebarOpen(false)}
className="text-gray-500 hover:text-gray-700"
>
<XIcon className="h-6 w-6" />
</button>
</div>
<nav className="flex-1 px-2 py-4 space-y-1">
{navigationItems.map((item) => {
const Icon = item.icon;
const isActive = router.pathname === item.href;
return (
<Link key={item.name} href={item.href}>
<a
className={`group flex items-center px-2 py-2 text-sm font-medium rounded-md transition-colors ${
isActive
? 'bg-blue-100 text-blue-900 border-r-2 border-blue-500'
: 'text-gray-700 hover:bg-gray-100 hover:text-gray-900'
}`}
onClick={() => setSidebarOpen(false)}
>
<Icon className="mr-3 h-5 w-5 flex-shrink-0" />
{item.name}
</a>
</Link>
);
})}
</nav>
</div>
</div>
{/* Desktop layout */}
<div className="flex">
{/* Desktop sidebar */}
<div className="hidden lg:flex lg:flex-shrink-0">
<div className="flex w-64 flex-col">
<div className="flex min-h-0 flex-1 flex-col bg-white border-r border-gray-200 shadow-sm">
<div className="flex h-16 items-center justify-center px-4 border-b border-gray-200 bg-gradient-to-r from-blue-600 to-blue-700">
<img className="h-8 w-auto filter brightness-0 invert" src="/logo.png" alt="Wastpol" />
{/* <span className="ml-2 text-lg font-semibold text-white">Wastpol</span> */}
</div>
<div className="flex flex-1 flex-col overflow-y-auto">
<nav className="flex-1 px-2 py-4 space-y-1">
{navigationItems.map((item) => {
const Icon = item.icon;
const isActive = router.pathname === item.href;
return (
<Link key={item.name} href={item.href}>
<a
className={`group flex items-center px-2 py-2 text-sm font-medium rounded-md transition-all duration-200 ${
isActive
? 'bg-blue-100 text-blue-900 border-r-2 border-blue-500 shadow-sm'
: 'text-gray-700 hover:bg-gray-100 hover:text-gray-900 hover:shadow-sm'
}`}
>
<Icon className={`mr-3 h-5 w-5 flex-shrink-0 transition-colors ${
isActive ? 'text-blue-600' : 'text-gray-400 group-hover:text-gray-500'
}`} />
{item.name}
</a>
</Link>
);
})}
</nav>
</div>
</div>
</div>
</div>
{/* Main content */}
<div className="flex flex-1 flex-col">
{/* Top navbar */}
<div className="relative z-10 flex h-16 flex-shrink-0 bg-white shadow-sm border-b border-gray-200">
<button
onClick={() => setSidebarOpen(true)}
className="border-r border-gray-200 px-4 text-gray-500 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-blue-500 lg:hidden"
>
<MenuIcon className="h-6 w-6" />
</button>
<div className="flex flex-1 justify-between px-4 sm:px-6 lg:px-8">
<div className="flex flex-1">
{/* You can add search or breadcrumbs here */}
</div>
{session && (
<div className="ml-4 flex items-center md:ml-6">
<div className="flex items-center space-x-4">
<div className="flex items-center space-x-2 text-sm text-gray-700">
<UserIcon className="h-5 w-5 text-gray-400" />
<span className="hidden sm:block">{session.user.email}</span>
</div>
<button
onClick={() => signOut()}
className="inline-flex items-center px-3 py-2 border border-gray-300 shadow-sm text-sm leading-4 font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 transition-colors"
>
<LogoutIcon className="h-4 w-4 mr-2" />
Wyloguj
</button>
</div>
</div>
)}
</div>
</div>
{/* Page content */}
<main className="flex-1 overflow-y-auto">
{children}
</main>
</div>
</div>
</div>
);
}

179
components/ui/components.js Normal file
View File

@@ -0,0 +1,179 @@
import React from 'react';
export const Card = ({ children, className = '', ...props }) => (
<div
className={`bg-white overflow-hidden shadow-sm border border-gray-200 rounded-xl transition-all duration-200 hover:shadow-md ${className}`}
{...props}
>
{children}
</div>
);
export const CardHeader = ({ children, className = '', ...props }) => (
<div className={`px-6 py-4 border-b border-gray-200 ${className}`} {...props}>
{children}
</div>
);
export const CardContent = ({ children, className = '', ...props }) => (
<div className={`px-6 py-4 ${className}`} {...props}>
{children}
</div>
);
export const CardTitle = ({ children, className = '', ...props }) => (
<h3 className={`text-lg font-semibold text-gray-900 ${className}`} {...props}>
{children}
</h3>
);
export const CardDescription = ({ children, className = '', ...props }) => (
<p className={`text-sm text-gray-600 mt-1 ${className}`} {...props}>
{children}
</p>
);
export const Button = ({
children,
variant = 'primary',
size = 'md',
className = '',
disabled = false,
loading = false,
...props
}) => {
const baseStyles = 'inline-flex items-center justify-center rounded-lg font-medium transition-all duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2 disabled:opacity-50 disabled:cursor-not-allowed';
const variants = {
primary: 'bg-blue-600 text-white hover:bg-blue-700 focus:ring-blue-500 shadow-sm hover:shadow-md',
secondary: 'bg-gray-100 text-gray-900 hover:bg-gray-200 focus:ring-gray-500 border border-gray-300',
outline: 'border border-gray-300 text-gray-700 bg-white hover:bg-gray-50 focus:ring-blue-500',
ghost: 'text-gray-700 hover:bg-gray-100 focus:ring-gray-500',
danger: 'bg-red-600 text-white hover:bg-red-700 focus:ring-red-500',
};
const sizes = {
sm: 'px-3 py-2 text-sm',
md: 'px-4 py-2 text-sm',
lg: 'px-6 py-3 text-base',
};
return (
<button
className={`${baseStyles} ${variants[variant]} ${sizes[size]} ${className}`}
disabled={disabled || loading}
{...props}
>
{loading && (
<svg className="animate-spin -ml-1 mr-2 h-4 w-4" fill="none" viewBox="0 0 24 24">
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4" />
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z" />
</svg>
)}
{children}
</button>
);
};
export const Input = ({ label, error, className = '', ...props }) => (
<div className="space-y-1">
{label && (
<label className="block text-sm font-medium text-gray-700">
{label}
</label>
)}
<input
className={`block w-full rounded-lg border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 transition-colors ${
error ? 'border-red-300 focus:border-red-500 focus:ring-red-500' : ''
} ${className}`}
{...props}
/>
{error && (
<p className="text-sm text-red-600">{error}</p>
)}
</div>
);
export const Textarea = ({ label, error, className = '', ...props }) => (
<div className="space-y-1">
{label && (
<label className="block text-sm font-medium text-gray-700">
{label}
</label>
)}
<textarea
className={`block w-full rounded-lg border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 transition-colors ${
error ? 'border-red-300 focus:border-red-500 focus:ring-red-500' : ''
} ${className}`}
{...props}
/>
{error && (
<p className="text-sm text-red-600">{error}</p>
)}
</div>
);
export const Badge = ({ children, variant = 'default', className = '' }) => {
const variants = {
default: 'bg-gray-100 text-gray-800',
success: 'bg-green-100 text-green-800',
warning: 'bg-yellow-100 text-yellow-800',
error: 'bg-red-100 text-red-800',
info: 'bg-blue-100 text-blue-800',
};
return (
<span className={`inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium ${variants[variant]} ${className}`}>
{children}
</span>
);
};
export const Alert = ({ children, variant = 'info', className = '' }) => {
const variants = {
info: 'bg-blue-50 border-blue-200 text-blue-800',
success: 'bg-green-50 border-green-200 text-green-800',
warning: 'bg-yellow-50 border-yellow-200 text-yellow-800',
error: 'bg-red-50 border-red-200 text-red-800',
};
return (
<div className={`border-l-4 p-4 rounded-lg ${variants[variant]} ${className}`}>
{children}
</div>
);
};
export const Tabs = ({ children, value, onValueChange }) => (
<div className="w-full">
<div className="border-b border-gray-200">
<nav className="-mb-px flex space-x-8">
{React.Children.map(children, (child, index) =>
React.cloneElement(child, {
isActive: value === index,
onClick: () => onValueChange(index)
})
)}
</nav>
</div>
</div>
);
export const TabsTrigger = ({ children, isActive, onClick }) => (
<button
onClick={onClick}
className={`whitespace-nowrap py-2 px-1 border-b-2 font-medium text-sm transition-colors ${
isActive
? 'border-blue-500 text-blue-600'
: 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300'
}`}
>
{children}
</button>
);
export const TabsContent = ({ children, isActive }) => (
<div className={`pt-6 ${isActive ? 'block' : 'hidden'}`}>
{children}
</div>
);

92
cross.py Normal file
View File

@@ -0,0 +1,92 @@
import sys
import ezdxf
import math
minx = 10000000000000
miny = 10000000000000
maxx = 0
maxy = 0
if len(sys.argv) > 0:
skala = int(sys.argv[1])
path = (sys.argv[2])
else:
skala = 50
alt = 0
fileName = path
def drawCross(x, y):
msp.add_text(
"X:"+str(x), dxfattribs={
"layer": "wsp", "height": 1, "style": "Arial"
}).set_pos((x+0.5, y+0.5))
msp.add_text(
"Y:"+str(y), dxfattribs={
"layer": "wsp", "height": 1, "style": "Arial"
}).set_pos((x+0.5, y-1.5))
msp.add_line((x-2.5, y), (x+2.5, y), dxfattribs={"layer": "siatka"})
msp.add_line((x, y-2.5), (x, y+2.5), dxfattribs={"layer": "siatka"})
try:
doc = ezdxf.readfile(fileName)
# iterate over all entities in modelspace
msp = doc.modelspace()
#doc.layers.add(name="siatka", color=0, linetype="SOLID")
for e in msp:
if e.dxftype() == "LINE":
if e.dxf.start[0] < minx:
minx = e.dxf.start[0]
if e.dxf.end[0] < minx:
minx = e.dxf.end[0]
if e.dxf.start[1] < miny:
miny = e.dxf.start[1]
if e.dxf.end[1] < miny:
miny = e.dxf.end[1]
if e.dxf.start[0] > maxx:
maxx = e.dxf.start[0]
if e.dxf.end[0] > maxx:
maxx = e.dxf.end[0]
if e.dxf.start[1] > maxy:
maxy = e.dxf.start[1]
if e.dxf.end[1] > maxy:
maxy = e.dxf.end[1]
elif e.dxftype() == "POLYLINE":
for point in e.points():
if point[0] < minx:
minx = point[0]
if point[1] < miny:
miny = point[1]
if point[0] > maxx:
maxx = point[0]
if point[1] > maxy:
maxy = point[1]
ys = (math.floor(miny/100)*100) - (50 * alt)
ye = (math.ceil(maxy/100)*100) - (50 * alt)
xs = (math.floor((minx)/100)*100) - (50 * alt)
xe = (math.ceil((maxx)/100)*100) - (50 * alt)
lenX = (xe - xs) + 100
lenY = (ye - ys) + 100
for x in range(0, int(lenX/skala)):
for y in range(0, int(lenY/skala)):
drawCross(xs+(x*skala),ys+(y*skala))
print(fileName)
doc.saveas(fileName[:-4]+"_s.dxf")
doc.saveas("public/cross.dxf")
except IOError:
print(f"Not a DXF file or a generic I/O error.")
sys.exit(1)
except ezdxf.DXFStructureError:
print(f"Invalid or corrupted DXF file.")
sys.exit(2)

BIN
dry_input_15.docx Normal file

Binary file not shown.

BIN
dry_input_3.docx Normal file

Binary file not shown.

View File

@@ -2,7 +2,7 @@ module.exports = {
apps : [{
name:'kroj',
script: 'npm',
args:'start',
args:'run start',
watch: ['pages'],
ignore_watch : ['public', 'P.txt']
}]

9650
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -7,23 +7,31 @@
"export": "next build && next export",
"start": "next start",
"lint": "next lint",
"deploy": "git pull && npm i && next build && pm2 restart kroj"
"deploy": "git pull && npm i && next build && pm2 restart kroj",
"buildRun": "next build && pm2 restart kroj"
},
"dependencies": {
"@heroicons/react": "^2.2.0",
"@socialgouv/matomo-next": "^1.3.0",
"@supercharge/request-ip": "^1.1.2",
"@tailwindcss/forms": "^0.5.10",
"axios": "^0.26.0",
"child_process": "^1.0.2",
"cors": "^2.8.5",
"date-fns": "^2.30.0",
"docxtemplater": "^3.40.3",
"evergreen-ui": "^6.8.2",
"formidable": "^2.1.1",
"next": "^12.0.10",
"next-auth": "^4.2.1",
"nodemailer": "^6.7.2",
"pizzip": "^3.1.4",
"react": "17.0.2",
"react-datepicker": "^4.21.0",
"react-dom": "17.0.2"
},
"devDependencies": {
"autoprefixer": "^10.4.0",
"cypress": "^9.5.0",
"eslint": "8.4.1",
"eslint-config-next": "12.0.7",
"postcss": "^8.4.5",

56
pages/api/external.js Normal file
View File

@@ -0,0 +1,56 @@
import Cors from "cors";
import fs from "fs";
// Initializing the cors middleware
// You can read more about the available options here: https://github.com/expressjs/cors#configuration-options
const cors = Cors({
methods: ["POST"],
});
// Helper method to wait for a middleware to execute before continuing
// And to throw an error when an error happens in a middleware
function runMiddleware(req, res, fn) {
return new Promise((resolve, reject) => {
fn(req, res, (result) => {
if (result instanceof Error) {
return reject(result);
}
return resolve(result);
});
});
}
export default async function handler(req, res) {
// Run the middleware
await runMiddleware(req, res, cors);
const id = Math.floor(Math.random() * 90000) + 10000;
const textData = req.body;
let data = textData.split(",");
let text = "Próbkowanie: 1\nSegment 0: w dół\nX: Y, Z\n";
let i = 1;
for (let snippet of data) {
if (i % 3 != 0) {
text += snippet + ",";
} else {
text += snippet + "\n";
}
i += 1;
}
// console.log(text);
fs.writeFile(`externals/${id}.txt`, text, (err) => {
if (err) {
res.status(418);
throw err;
}
});
// console.log(textData);
res.status(200).send({ url: `https://test.wastpol.pl/?external=tru&id=${id}` });
}

86
pages/api/generateDocx.js Normal file
View File

@@ -0,0 +1,86 @@
import PizZip from "pizzip";
import Docxtemplater from "docxtemplater";
import fs from "fs";
import path from "path";
export default async (req, res) => {
if (req.method === "POST") {
// Get the data from the request body
const data = req.body;
// log stats
let ip = req.body.ip;
if (ip === undefined)
ip = req.headers["x-forwarded-for"] + " on " + req.headers["user-agent"];
let date_time = new Date();
let string =
fs.readFileSync("ground_log.txt", "utf8") +
"\n" +
"[" +
date_time +
"]" +
ip +
" : " +
data.geo_data +
" : " +
data.in_city;
fs.writeFile("ground_log.txt", string, function (err) {
if (err) {
return console.error(err);
}
});
//get wet coefficient
let input_docx;
if (data.wet_coef == 1.6) {
if (data.rod_len == 2) input_docx = "wet_input_15.docx";
else if (data.rod_len == 3) input_docx = "wet_input_3.docx";
} else {
if (data.rod_len == 2) input_docx = "dry_input_15.docx";
else if (data.rod_len == 3) input_docx = "dry_input_3.docx";
}
// Load the docx file as binary content
const content = fs.readFileSync(
path.resolve(process.cwd(), input_docx),
"binary"
);
const zip = new PizZip(content);
const doc = new Docxtemplater(zip, {
paragraphLoop: true,
linebreaks: true,
});
try {
// Render the document with data
console.log(data);
doc.render(data);
// Generate the document as a buffer
const buf = doc.getZip().generate({
type: "nodebuffer",
compression: "DEFLATE",
});
// Set headers for downloading the file
res.setHeader("Content-Disposition", "attachment; filename=opis.docx");
res.setHeader(
"Content-Type",
"application/vnd.openxmlformats-officedocument.wordprocessingml.document"
);
// Send the buffer as a response
res.send(buf);
} catch (error) {
// Handle errors
console.error(error);
res.status(500).json({ error: error.message });
}
} else {
// Handle non-POST requests
res.setHeader("Allow", ["POST"]);
res.status(405).end(`Method ${req.method} Not Allowed`);
}
};

39
pages/api/generateDxf.js Normal file
View File

@@ -0,0 +1,39 @@
import { exec } from 'child_process';
import fs from 'fs';
import path from 'path';
export default function handler(req, res) {
if (req.method === 'POST') {
// Get the input data from the request body
const data = req.body;
// Prepare arguments to pass to the Python script if needed
const scriptArgs = data.args || [];
console.log(scriptArgs)
const Arguments = "\"" + scriptArgs.join('" "') + "\"";
// Run the Python script and pass the arguments
exec(`python3 uziom.py ${Arguments}`, (error, stdout, stderr) => {
if (error) {
// Handle execution errors
console.error(`exec error: ${error}`);
return res.status(500).json({ message: 'Internal server error' });
}
// The output file needs to have been created by the Python script
const filePath = path.resolve('./public/uziom.dxf');
const fileName = 'uziom.dxf';
// Set headers for file download
res.setHeader('Content-Disposition', `attachment; filename=${fileName}`);
res.setHeader('Content-Type', 'application/dxf');
// Read and send the file data
const fileStream = fs.createReadStream(filePath);
fileStream.pipe(res);
});
} else {
// Handle any other HTTP methods if necessary
res.status(405).json({ message: 'Method not allowed' });
}
}

11
pages/api/readtext.js Normal file
View File

@@ -0,0 +1,11 @@
import fs from "fs";
export default function (req, res) {
fs.readFile(`externals/${req.body.id}.txt`, (err, data) => {
if (err) throw err;
res
.status(200)
.send({ data: data.toString().slice(0, data.toString().length - 1) });
});
}

191
pages/api/spadki.js Normal file
View File

@@ -0,0 +1,191 @@
import { getSession } from 'next-auth/react';
export default async function handler(req, res) {
// Authentication check
const session = await getSession({ req });
if (!session) {
return res.status(401).json({ error: 'Unauthorized' });
}
if (req.method !== 'POST') {
return res.status(405).json({ error: 'Method not allowed' });
}
try {
const { objects, cables } = req.body;
// Validate input data
if (!objects || !cables) {
return res.status(400).json({ error: 'Missing objects or cables data' });
}
// Find transformer (source)
const transformer = objects.find(obj => obj.type === 'transformer');
if (!transformer) {
return res.status(400).json({ error: 'No transformer found in the system' });
}
// Calculate voltage drops for each path
const results = calculateVoltageDrops(transformer, objects, cables);
res.status(200).json({
success: true,
data: results,
timestamp: new Date().toISOString()
});
} catch (error) {
console.error('Voltage drop calculation error:', error);
res.status(500).json({
error: 'Internal server error',
details: process.env.NODE_ENV === 'development' ? error.message : undefined
});
}
}
function calculateVoltageDrops(transformer, objects, cables) {
const results = {
transformer: transformer,
paths: [],
summary: {
totalPower: 0,
maxVoltageDrop: 0,
minVoltage: transformer.bottomVoltage
}
};
// Cable resistance values (Ω/km) - simplified values for common cable types
const cableResistance = {
'YAKY': {
16: 1.15, 25: 0.727, 35: 0.524, 50: 0.387, 70: 0.268, 95: 0.193, 120: 0.153, 150: 0.124, 185: 0.099, 240: 0.075
},
'NA2XY-J': {
16: 1.15, 25: 0.727, 35: 0.524, 50: 0.387, 70: 0.268, 95: 0.193, 120: 0.153, 150: 0.124, 185: 0.099, 240: 0.075
},
'AL': {
16: 1.91, 25: 1.20, 35: 0.868, 50: 0.641, 70: 0.443, 95: 0.320, 120: 0.253, 150: 0.206, 185: 0.164, 240: 0.125
},
'AsXSn': {
16: 1.91, 25: 1.20, 35: 0.868, 50: 0.641, 70: 0.443, 95: 0.320, 120: 0.253, 150: 0.206, 185: 0.164, 240: 0.125
}
};
// Find all paths from transformer to end connections
const paths = findAllPaths(transformer, objects, cables);
paths.forEach((path, index) => {
let totalVoltageDrop = 0;
let pathPower = 0;
const pathDetails = [];
// Calculate for each segment in the path
for (let i = 0; i < path.length - 1; i++) {
const fromNode = path[i];
const toNode = path[i + 1];
// Find the cable connecting these nodes
const cable = cables.find(c =>
(c.from === fromNode.id && c.to === toNode.id) ||
(c.from === toNode.id && c.to === fromNode.id)
);
if (cable) {
// Get cable resistance
const resistance = getCableResistance(cable, cableResistance);
// Calculate current (simplified - assuming balanced load)
const nodePower = toNode.powerRating || 0;
const current = nodePower > 0 ? (nodePower * 1000) / (Math.sqrt(3) * transformer.bottomVoltage * 0.9) : 0; // cos φ = 0.9
// Calculate voltage drop for this segment
const segmentVoltageDrop = Math.sqrt(3) * current * resistance * (cable.length / 1000); // length in km
totalVoltageDrop += segmentVoltageDrop;
pathPower += nodePower;
pathDetails.push({
from: fromNode.name,
to: toNode.name,
cable: {
label: cable.label,
type: cable.cableType,
crossSection: cable.crossSection,
length: cable.length
},
current: current.toFixed(2),
resistance: resistance.toFixed(4),
voltageDrop: segmentVoltageDrop.toFixed(2),
power: nodePower
});
}
}
const finalVoltage = transformer.bottomVoltage - totalVoltageDrop;
const voltageDropPercentage = (totalVoltageDrop / transformer.bottomVoltage) * 100;
results.paths.push({
id: index + 1,
endNode: path[path.length - 1].name,
totalVoltageDrop: totalVoltageDrop.toFixed(2),
finalVoltage: finalVoltage.toFixed(2),
voltageDropPercentage: voltageDropPercentage.toFixed(2),
pathPower: pathPower,
segments: pathDetails,
isValid: voltageDropPercentage <= 5 // 5% is typical limit
});
results.summary.totalPower += pathPower;
if (totalVoltageDrop > results.summary.maxVoltageDrop) {
results.summary.maxVoltageDrop = totalVoltageDrop;
results.summary.minVoltage = finalVoltage;
}
});
return results;
}
function findAllPaths(transformer, objects, cables) {
const paths = [];
const visited = new Set();
function dfs(currentNode, currentPath) {
visited.add(currentNode.id);
currentPath.push(currentNode);
// If this is an end connection, save the path
if (currentNode.type === 'end') {
paths.push([...currentPath]);
} else {
// Find all connected nodes
const connectedCables = cables.filter(cable =>
cable.from === currentNode.id || cable.to === currentNode.id
);
connectedCables.forEach(cable => {
const nextNodeId = cable.from === currentNode.id ? cable.to : cable.from;
const nextNode = objects.find(obj => obj.id === nextNodeId);
if (nextNode && !visited.has(nextNodeId)) {
dfs(nextNode, currentPath);
}
});
}
currentPath.pop();
visited.delete(currentNode.id);
}
dfs(transformer, []);
return paths;
}
function getCableResistance(cable, resistanceTable) {
const cableType = cable.cableType || 'YAKY';
const crossSection = cable.crossSection || 25;
if (resistanceTable[cableType] && resistanceTable[cableType][crossSection]) {
return resistanceTable[cableType][crossSection];
}
// Default resistance if not found
return 1.0;
}

View File

@@ -4,7 +4,7 @@ export default function (req, res) {
//console.log(req.body);
//file
let textData = req.body.profil;
let replacedData = textData.replace("<EFBFBD>", "o").replace("<EFBFBD>", "e");
let replacedData = textData.replace(" ", "o").replace(" ", "e");
console.log(replacedData);
var fs = require("fs");
@@ -14,10 +14,32 @@ export default function (req, res) {
}
});
// log stats
let ip = req.body.ip;
if (ip === undefined)
ip = req.headers["x-forwarded-for"] + " on " + req.headers["user-agent"];
let date_time = new Date();
let string =
fs.readFileSync("profile_log.txt", "utf8") +
"\n" +
"[" +
date_time +
"]" +
ip +
" : " +
replacedData
fs.writeFile("profile_log.txt", string, function (err) {
if (err) {
return console.error(err);
}
});
//py
let fileName = Math.floor(Math.random() * 9999) + 1000;
const python = spawn("python3", ["a.py", req.body.arguments.scale, fileName]);
const python = spawn("python3", ["a.py", req.body.arguments.scale, req.body.arguments.elementOne, req.body.arguments.elementTwo]);
let dataToSend;
python.stdout.on("data", function (data) {

105
pages/api/upload.js Normal file
View File

@@ -0,0 +1,105 @@
import fs from "fs";
import path from "path";
// import nextConnect from 'next-connect';
// import { sendFile } from 'next/dist/server/send-file';
import formidable from "formidable";
import { spawn } from "child_process";
export const config = {
api: {
bodyParser: false,
},
};
export default async function (req, res) {
const form = new formidable.IncomingForm();
try {
form.parse(req, async (err, fields, files) => {
if (err) {
return res.status(400).json({ message: err.message });
}
console.log('doing the saving')
const { filepath, originalFilename } = files.file;
const dateNow = Date.now()
const newFilename = `${Date.now()}-${originalFilename}`;
const newPath = path.join(
process.cwd(),
"public",
"uploads",
newFilename
);
console.log('i tried so hart')
const readStream = fs.createReadStream(filepath);
const writeStream = fs.createWriteStream(newPath);
readStream.on('error', function(err) {
console.error('Error while reading file.', err);
res.status(500).send('Error while uploading file.');
});
writeStream.on('error', function(err) {
console.error('Error while writing file.', err);
res.status(500).send('Error while uploading file.');
});
writeStream.on('finish', function() {
console.log('File copied successfully');
let dataToSend;
python.stdout.on("data", function (data) {
console.log("Pipe data from python script ...");
dataToSend = data.toString();
console.log(dataToSend);
});
python.stderr.on("data", (data) => {
console.error(`stderr: ${data}`);
res.send("Py Error: " + data);
});
// in close event we are sure that stream from child process is closed
python.on("close", (code) => {
console.log(`child process close all stdio with code ${code}`);
// send data to browser
console.log(dataToSend);
console.log("done");
try {
// const handler = nextConnect();
// handler.get(async (req, res) => {
const filename = newFilename; // get the filename from the query string
const filePath = path.join(process.cwd(), 'public', 'uploads', filename);
console.log(newFilename, filename, filePath, dataToSend)
res.status(200).send({ filename: newFilename, filepath:filePath, data: dataToSend });
// sendFile(req, filePath); // send the file as a response
// });
} catch (e) {
console.log("child process end");
}
});
});
readStream.pipe(writeStream);
console.log('and got so far')
let fileName = Math.floor(Math.random() * 9999) + 1000;
console.log('starting the engines', newPath)
const python = spawn("python3", ["cross.py", 50, newPath]);
console.log('oui')
// return res.status(200).json({ message: "File uploaded successfully" });
});
} catch (err) {
console.log("err: ", err)
return res.status(500).json({ message: err.message });
}
}

247
pages/cross.js Normal file
View File

@@ -0,0 +1,247 @@
import { useState, useCallback } from "react";
import { useSession, signIn } from "next-auth/react";
import Layout from "../components/ui/Layout";
import { Card, CardHeader, CardContent, CardTitle, CardDescription, Button, Alert } from "../components/ui/components";
import {
FileUploader,
FileCard,
} from "evergreen-ui";
import { CloudArrowUpIcon as CloudUploadIcon, ArrowDownTrayIcon as DownloadIcon, Squares2X2Icon as GridIcon } from '@heroicons/react/24/outline';
import axios from "axios";
export default function Cross() {
const { data: session } = useSession();
const [files, setFiles] = useState([]);
const [fileRejections, setFileRejections] = useState([]);
const [fileData, setFileData] = useState(null);
const [isLoading, setIsLoading] = useState(false);
const handleChange = useCallback((files) => setFiles([files[0]]), []);
const handleRejected = useCallback(
(fileRejections) => setFileRejections([fileRejections[0]]),
[]
);
const handleRemove = useCallback(() => {
setFiles([]);
setFileRejections([]);
}, []);
const handleSubmit = (e) => {
e.preventDefault();
setIsLoading(true);
console.log("Files:", files);
if (files.length === 0) {
console.log("No files selected");
setIsLoading(false);
return;
}
const formData = new FormData();
formData.append("files", files[0]);
axios
.post("/api/cross", formData, {
headers: {
"Content-Type": "multipart/form-data",
},
})
.then((response) => {
console.log(response.data);
setFileData(response.data);
setIsLoading(false);
})
.catch((error) => {
console.error(error);
setIsLoading(false);
});
};
const handleDownload = () => {
if (fileData) {
document.getElementById("down").download = fileData.filename;
document.getElementById("down").href = "cross.dxf";
document.getElementById("down").click();
}
};
if (session) {
return (
<Layout title="Wastpol - Generator siatki">
<div className="p-6 max-w-4xl mx-auto">
{/* Page Header */}
<div className="mb-8">
<div className="flex items-center space-x-3 mb-4">
<div className="p-3 bg-blue-100 rounded-lg">
<GridIcon className="w-8 h-8 text-blue-600" />
</div>
<div>
<h1 className="text-3xl font-bold text-gray-900">Generator siatki</h1>
<p className="text-gray-600">Dodaj siatkę do mapy w pliku DXF</p>
</div>
</div>
</div>
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8">
{/* Upload Section */}
<Card>
<CardHeader>
<CardTitle className="flex items-center space-x-2">
<CloudUploadIcon className="w-5 h-5 text-blue-600" />
<span>Prześlij plik</span>
</CardTitle>
<CardDescription>
Wybierz plik DXF do przetworzenia (maksymalnie 50 MB)
</CardDescription>
</CardHeader>
<CardContent>
<div className="space-y-4">
<FileUploader
label="Przeciągnij i upuść plik lub kliknij aby wybrać"
description="Obsługiwane formaty: DXF (max 50 MB)"
maxSizeInBytes={50 * 1024 ** 2}
maxFiles={1}
onChange={handleChange}
onRejected={handleRejected}
renderFile={(file) => {
const { name, size, type } = file;
const fileRejection = fileRejections.find(
(fileRejection) => fileRejection.file === file
);
const { message } = fileRejection || {};
return (
<FileCard
key={name}
isInvalid={fileRejection != null}
name={name}
onRemove={handleRemove}
sizeInBytes={size}
type={type}
validationMessage={message}
/>
);
}}
values={files}
/>
<Button
onClick={handleSubmit}
disabled={files.length === 0 || isLoading}
loading={isLoading}
className="w-full"
>
{isLoading ? 'Przetwarzanie...' : 'Dodaj siatkę'}
</Button>
</div>
</CardContent>
</Card>
{/* Results Section */}
<Card>
<CardHeader>
<CardTitle className="flex items-center space-x-2">
<DownloadIcon className="w-5 h-5 text-green-600" />
<span>Wynik</span>
</CardTitle>
<CardDescription>
Pobierz wygenerowany plik siatki
</CardDescription>
</CardHeader>
<CardContent>
{fileData ? (
<div className="space-y-4">
<Alert variant="success">
<div className="flex items-center space-x-2">
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" />
</svg>
<span>Plik został pomyślnie przetworzony!</span>
</div>
</Alert>
<Button
onClick={handleDownload}
variant="primary"
className="w-full"
>
<DownloadIcon className="w-5 h-5 mr-2" />
Pobierz siatką DXF
</Button>
</div>
) : (
<div className="text-center py-8">
<div className="mx-auto w-16 h-16 bg-gray-100 rounded-full flex items-center justify-center mb-4">
<GridIcon className="w-8 h-8 text-gray-400" />
</div>
<p className="text-gray-500">Prześlij plik aby rozpocząć przetwarzanie</p>
</div>
)}
</CardContent>
</Card>
</div>
{/* Instructions */}
<Card className="mt-8">
<CardHeader>
<CardTitle>Instrukcje</CardTitle>
</CardHeader>
<CardContent>
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
<div className="flex items-start space-x-3">
<div className="flex-shrink-0 w-8 h-8 bg-blue-100 rounded-full flex items-center justify-center">
<span className="text-sm font-semibold text-blue-600">1</span>
</div>
<div>
<h4 className="font-medium text-gray-900">Prześlij plik</h4>
<p className="text-sm text-gray-600">Wybierz plik DXF z projektem</p>
</div>
</div>
<div className="flex items-start space-x-3">
<div className="flex-shrink-0 w-8 h-8 bg-blue-100 rounded-full flex items-center justify-center">
<span className="text-sm font-semibold text-blue-600">2</span>
</div>
<div>
<h4 className="font-medium text-gray-900">Przetwarzaj</h4>
<p className="text-sm text-gray-600">Kliknij "Dodaj siatkę"</p>
</div>
</div>
<div className="flex items-start space-x-3">
<div className="flex-shrink-0 w-8 h-8 bg-blue-100 rounded-full flex items-center justify-center">
<span className="text-sm font-semibold text-blue-600">3</span>
</div>
<div>
<h4 className="font-medium text-gray-900">Pobierz</h4>
<p className="text-sm text-gray-600">Zapisz gotową siatkę DXF</p>
</div>
</div>
</div>
</CardContent>
</Card>
<a href="the.dxf" download="the.dxf" id="down" className="hidden" />
</div>
</Layout>
);
}
return (
<div className="min-h-screen bg-gradient-to-br from-blue-50 to-indigo-100 flex items-center justify-center p-4">
<Card className="w-full max-w-md">
<CardHeader className="text-center">
<div className="mx-auto w-16 h-16 bg-blue-100 rounded-full flex items-center justify-center mb-4">
<img src="/logo.png" alt="Wastpol" className="w-10 h-10" />
</div>
<CardTitle className="text-2xl">Zaloguj się</CardTitle>
<p className="text-gray-600 mt-2">Uzyskaj dostęp do generatora siatki</p>
</CardHeader>
<CardContent>
<Button onClick={() => signIn()} className="w-full" size="lg">
Zaloguj się
</Button>
</CardContent>
</Card>
</div>
);
}

247
pages/cross_new.js Normal file
View File

@@ -0,0 +1,247 @@
import { useState, useCallback } from "react";
import { useSession, signIn } from "next-auth/react";
import Layout from "../components/ui/Layout";
import { Card, CardHeader, CardContent, CardTitle, CardDescription, Button, Alert } from "../components/ui/components";
import {
FileUploader,
FileCard,
} from "evergreen-ui";
import { CloudArrowUpIcon as CloudUploadIcon, ArrowDownTrayIcon as DownloadIcon, Squares2X2Icon as GridIcon } from '@heroicons/react/24/outline';
import axios from "axios";
export default function Cross() {
const { data: session } = useSession();
const [files, setFiles] = useState([]);
const [fileRejections, setFileRejections] = useState([]);
const [fileData, setFileData] = useState(null);
const [isLoading, setIsLoading] = useState(false);
const handleChange = useCallback((files) => setFiles([files[0]]), []);
const handleRejected = useCallback(
(fileRejections) => setFileRejections([fileRejections[0]]),
[]
);
const handleRemove = useCallback(() => {
setFiles([]);
setFileRejections([]);
}, []);
const handleSubmit = (e) => {
e.preventDefault();
setIsLoading(true);
console.log("Files:", files);
if (files.length === 0) {
console.log("No files selected");
setIsLoading(false);
return;
}
const formData = new FormData();
formData.append("files", files[0]);
axios
.post("/api/cross", formData, {
headers: {
"Content-Type": "multipart/form-data",
},
})
.then((response) => {
console.log(response.data);
setFileData(response.data);
setIsLoading(false);
})
.catch((error) => {
console.error(error);
setIsLoading(false);
});
};
const handleDownload = () => {
if (fileData) {
document.getElementById("down").download = fileData.filename;
document.getElementById("down").href = "cross.dxf";
document.getElementById("down").click();
}
};
if (session) {
return (
<Layout title="Wastpol - Generator siatki">
<div className="p-6 max-w-4xl mx-auto">
{/* Page Header */}
<div className="mb-8">
<div className="flex items-center space-x-3 mb-4">
<div className="p-3 bg-blue-100 rounded-lg">
<GridIcon className="w-8 h-8 text-blue-600" />
</div>
<div>
<h1 className="text-3xl font-bold text-gray-900">Generator siatki</h1>
<p className="text-gray-600">Przekształć pliki DXF na siatki instalacyjne</p>
</div>
</div>
</div>
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8">
{/* Upload Section */}
<Card>
<CardHeader>
<CardTitle className="flex items-center space-x-2">
<CloudUploadIcon className="w-5 h-5 text-blue-600" />
<span>Prześlij plik</span>
</CardTitle>
<CardDescription>
Wybierz plik DXF do przetworzenia (maksymalnie 50 MB)
</CardDescription>
</CardHeader>
<CardContent>
<div className="space-y-4">
<FileUploader
label="Przeciągnij i upuść plik lub kliknij aby wybrać"
description="Obsługiwane formaty: DXF (max 50 MB)"
maxSizeInBytes={50 * 1024 ** 2}
maxFiles={1}
onChange={handleChange}
onRejected={handleRejected}
renderFile={(file) => {
const { name, size, type } = file;
const fileRejection = fileRejections.find(
(fileRejection) => fileRejection.file === file
);
const { message } = fileRejection || {};
return (
<FileCard
key={name}
isInvalid={fileRejection != null}
name={name}
onRemove={handleRemove}
sizeInBytes={size}
type={type}
validationMessage={message}
/>
);
}}
values={files}
/>
<Button
onClick={handleSubmit}
disabled={files.length === 0 || isLoading}
loading={isLoading}
className="w-full"
>
{isLoading ? 'Przetwarzanie...' : 'Dodaj siatkę'}
</Button>
</div>
</CardContent>
</Card>
{/* Results Section */}
<Card>
<CardHeader>
<CardTitle className="flex items-center space-x-2">
<DownloadIcon className="w-5 h-5 text-green-600" />
<span>Wynik</span>
</CardTitle>
<CardDescription>
Pobierz wygenerowany plik siatki
</CardDescription>
</CardHeader>
<CardContent>
{fileData ? (
<div className="space-y-4">
<Alert variant="success">
<div className="flex items-center space-x-2">
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" />
</svg>
<span>Plik został pomyślnie przetworzony!</span>
</div>
</Alert>
<Button
onClick={handleDownload}
variant="primary"
className="w-full"
>
<DownloadIcon className="w-5 h-5 mr-2" />
Pobierz siatką DXF
</Button>
</div>
) : (
<div className="text-center py-8">
<div className="mx-auto w-16 h-16 bg-gray-100 rounded-full flex items-center justify-center mb-4">
<GridIcon className="w-8 h-8 text-gray-400" />
</div>
<p className="text-gray-500">Prześlij plik aby rozpocząć przetwarzanie</p>
</div>
)}
</CardContent>
</Card>
</div>
{/* Instructions */}
<Card className="mt-8">
<CardHeader>
<CardTitle>Instrukcje</CardTitle>
</CardHeader>
<CardContent>
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
<div className="flex items-start space-x-3">
<div className="flex-shrink-0 w-8 h-8 bg-blue-100 rounded-full flex items-center justify-center">
<span className="text-sm font-semibold text-blue-600">1</span>
</div>
<div>
<h4 className="font-medium text-gray-900">Prześlij plik</h4>
<p className="text-sm text-gray-600">Wybierz plik DXF z projektem</p>
</div>
</div>
<div className="flex items-start space-x-3">
<div className="flex-shrink-0 w-8 h-8 bg-blue-100 rounded-full flex items-center justify-center">
<span className="text-sm font-semibold text-blue-600">2</span>
</div>
<div>
<h4 className="font-medium text-gray-900">Przetwarzaj</h4>
<p className="text-sm text-gray-600">Kliknij "Dodaj siatkę"</p>
</div>
</div>
<div className="flex items-start space-x-3">
<div className="flex-shrink-0 w-8 h-8 bg-blue-100 rounded-full flex items-center justify-center">
<span className="text-sm font-semibold text-blue-600">3</span>
</div>
<div>
<h4 className="font-medium text-gray-900">Pobierz</h4>
<p className="text-sm text-gray-600">Zapisz gotową siatkę DXF</p>
</div>
</div>
</div>
</CardContent>
</Card>
<a href="the.dxf" download="the.dxf" id="down" className="hidden" />
</div>
</Layout>
);
}
return (
<div className="min-h-screen bg-gradient-to-br from-blue-50 to-indigo-100 flex items-center justify-center p-4">
<Card className="w-full max-w-md">
<CardHeader className="text-center">
<div className="mx-auto w-16 h-16 bg-blue-100 rounded-full flex items-center justify-center mb-4">
<img src="/logo.png" alt="Wastpol" className="w-10 h-10" />
</div>
<CardTitle className="text-2xl">Zaloguj się</CardTitle>
<p className="text-gray-600 mt-2">Uzyskaj dostęp do generatora siatki</p>
</CardHeader>
<CardContent>
<Button onClick={() => signIn()} className="w-full" size="lg">
Zaloguj się
</Button>
</CardContent>
</Card>
</div>
);
}

View File

@@ -1,93 +1,129 @@
import Head from "next/head";
import styles from "../styles/Home.module.css";
import Header from "../components/templates/header";
import { useState } from "react";
import { useSession, signIn } from "next-auth/react";
import Layout from "../components/ui/Layout";
import { Card, CardHeader, CardContent, CardTitle, Button, Tabs, TabsTrigger, TabsContent } from "../components/ui/components";
import Generator from "../components/templates/generator";
import Manual from "../components/templates/manual";
import Nav from "../components/templates/nav";
import UserTop from "../components/templates/userTop";
import Footer from "../components/templates/footer";
import { useState } from "react";
import { useSession, signIn, signOut } from "next-auth/react";
import { Pane, Button, Tab, Tablist } from "evergreen-ui";
import { ChartBarIcon, PencilIcon, ArrowRightOnRectangleIcon as LoginIcon } from '@heroicons/react/24/outline';
export default function Home() {
const { data: session } = useSession();
const [selectedIndex, setSelectedIndex] = useState(0);
const [tabs] = useState(["auto", "manual"]);
const [realTabs] = useState([Generator, Manual]);
const [selectedTab, setSelectedTab] = useState(0);
if (session) {
return (
<div className="">
<Head>
<title>Wastpol</title>
</Head>
<div className="flex md:flex-row flex-col justify-between px-8 mt-2">
<Nav />
<UserTop session={session} />
</div>
<main className="flex flex-1 flex-col items-center">
<div className="flex flex-col items-center p-8 mt-12 rounded-md shadow-md transition-all duration-500 hover:shadow-xl">
<Header />
<Tablist>
{tabs.map((tab, index) => (
<Tab
key={tab}
id={tab}
onSelect={() => setSelectedIndex(index)}
isSelected={index === selectedIndex}
>
{tab}
</Tab>
))}
</Tablist>
<Pane>
{realTabs.map((tab, index) =>
index == 1 ? (
<Pane
key={tab}
id={`panel-${tab}`}
role="tabpanel"
display={index === selectedIndex ? "block" : "none"}
>
<Manual />
</Pane>
) : (
<Pane
key={tab}
id={`panel-${tab}`}
role="tabpanel"
display={index === selectedIndex ? "block" : "none"}
>
<Generator />
</Pane>
)
)}
</Pane>
<Layout title="Wastpol - Profil przekroju terenu">
<div className="p-6 max-w-7xl mx-auto">
{/* Page Header */}
<div className="mb-8">
<h1 className="text-3xl font-bold text-gray-900 mb-2">
Generator profilu przekroju terenu
</h1>
<p className="text-gray-600">
Stwórz profil terenu
</p>
</div>
</main>
</div>
{/* Main Content Card */}
<Card className="mb-6">
<CardHeader className="border-b bg-gray-50">
<Tabs value={selectedTab} onValueChange={setSelectedTab}>
<TabsTrigger>
<ChartBarIcon className="w-5 h-5 mr-2" />
Automatyczny
</TabsTrigger>
<TabsTrigger>
<PencilIcon className="w-5 h-5 mr-2" />
Ręczny
</TabsTrigger>
</Tabs>
</CardHeader>
<CardContent className="p-0">
<TabsContent isActive={selectedTab === 0}>
<div className="p-6">
<Generator />
</div>
</TabsContent>
<TabsContent isActive={selectedTab === 1}>
<div className="p-6">
<Manual />
</div>
</TabsContent>
</CardContent>
</Card>
{/* Info Cards */}
{/* <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
<Card>
<CardContent>
<div className="flex items-center space-x-3">
<div className="p-2 bg-blue-100 rounded-lg">
<ChartBarIcon className="w-6 h-6 text-blue-600" />
</div>
<div>
<h3 className="font-semibold text-gray-900">Automatyczny</h3>
<p className="text-sm text-gray-600">Import danych z Geoportalu</p>
</div>
</div>
</CardContent>
</Card>
<Card>
<CardContent>
<div className="flex items-center space-x-3">
<div className="p-2 bg-green-100 rounded-lg">
<PencilIcon className="w-6 h-6 text-green-600" />
</div>
<div>
<h3 className="font-semibold text-gray-900">Ręczny</h3>
<p className="text-sm text-gray-600">Wprowadź punkty manualnie</p>
</div>
</div>
</CardContent>
</Card>
<Card>
<CardContent>
<div className="flex items-center space-x-3">
<div className="p-2 bg-purple-100 rounded-lg">
<svg className="w-6 h-6 text-purple-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M9 19l3 3m0 0l3-3m-3 3V10" />
</svg>
</div>
<div>
<h3 className="font-semibold text-gray-900">Export DXF</h3>
<p className="text-sm text-gray-600">Pobierz gotowy rysunek</p>
</div>
</div>
</CardContent>
</Card>
</div> */}
</div>
</Layout>
);
}
return (
<div className="grid place-items-center h-screen">
<Head>
<title>Wastpol</title>
</Head>
<div className="flex flex-col justify-center">
<h2 className="p-2">Nie zalogowano</h2>
<br></br>
<Button
onClick={() => signIn()}
appearance="primary"
// className="p-2 bg-slate-200 rounded-md"
>
Zaloguj
</Button>
</div>
<div className="min-h-screen bg-gradient-to-br from-blue-50 to-indigo-100 flex items-center justify-center p-4">
<Card className="w-full max-w-md">
<CardHeader className="text-center">
<div className="mx-auto w-16 h-16 bg-blue-100 rounded-full flex items-center justify-center mb-4">
<img src="/logo.png" alt="Wastpol" className="w-10 h-10" />
</div>
<CardTitle className="text-2xl">Zaloguj się</CardTitle>
</CardHeader>
<CardContent>
<Button
onClick={() => signIn()}
className="w-full"
size="lg"
>
<LoginIcon className="w-5 h-5 mr-2" />
Zaloguj się
</Button>
</CardContent>
</Card>
</div>
);
}

157
pages/pdf.js Normal file
View File

@@ -0,0 +1,157 @@
import Head from "next/head";
import { useState, useCallback } from "react";
import styles from "../styles/Home.module.css";
import Header from "../components/templates/header";
import Nav from "../components/templates/nav";
import UserTop from "../components/templates/userTop";
import Content from "../components/templates/content";
import Footer from "../components/templates/footer";
import { useSession, signIn, signOut } from "next-auth/react";
import {
Pane,
TextInputField,
TextareaField,
Button,
BuildIcon,
toaster,
Alert,
FileUploader,
FileCard,
} from "evergreen-ui";
export default function Pdf() {
const { data: session } = useSession();
const [files, setFiles] = useState([]);
const [fileRejections, setFileRejections] = useState([]);
const handleChange = useCallback((files) => setFiles([files[0]]), []);
const handleRejected = useCallback(
(fileRejections) => setFileRejections([fileRejections[0]]),
[]
);
const handleRemove = useCallback(() => {
setFiles([]);
setFileRejections([]);
}, []);
if (session) {
return (
<div className="">
<Head>
<title>Wastpol</title>
</Head>
<div className="flex md:flex-row flex-col justify-between px-8 mt-2">
<Nav />
<UserTop session={session} />
</div>
<main className="flex flex-1 flex-col items-center">
<Pane maxWidth={654}>
<FileUploader
label="Upload File"
description="You can upload 1 file. File can be up to 50 MB."
maxSizeInBytes={50 * 1024 ** 2}
maxFiles={1}
onChange={handleChange}
onRejected={handleRejected}
renderFile={(file) => {
const { name, size, type } = file;
const fileRejection = fileRejections.find(
(fileRejection) => fileRejection.file === file
);
const { message } = fileRejection || {};
return (
<FileCard
key={name}
isInvalid={fileRejection != null}
name={name}
onRemove={handleRemove}
sizeInBytes={size}
type={type}
validationMessage={message}
/>
);
}}
values={files}
/>
</Pane>
<Button
marginY={8}
marginRight={12}
appearance="default"
iconAfter={BuildIcon}
onClick={(e) => {
console.log("louding begins");
}}
>
Przycisk
</Button>
</main>
</div>
);
}
return (
<div className="grid place-items-center h-screen">
<Head>
<title>Wastpol</title>
</Head>
<div className="flex flex-col justify-center">
<h2 className="p-2">Nie zalogowano</h2>
<br></br>
<Button
onClick={() => signIn()}
appearance="primary"
// className="p-2 bg-slate-200 rounded-md"
>
Zaloguj
</Button>
</div>
</div>
);
}
function FileUploaderSingleUploadDemo() {
const [files, setFiles] = useState([]);
const [fileRejections, setFileRejections] = useState([]);
const handleChange = useCallback((files) => setFiles([files[0]]), []);
const handleRejected = useCallback(
(fileRejections) => setFileRejections([fileRejections[0]]),
[]
);
const handleRemove = useCallback(() => {
setFiles([]);
setFileRejections([]);
}, []);
return (
<Pane maxWidth={654}>
<FileUploader
label="Upload File"
description="You can upload 1 file. File can be up to 50 MB."
maxSizeInBytes={50 * 1024 ** 2}
maxFiles={1}
onChange={handleChange}
onRejected={handleRejected}
renderFile={(file) => {
const { name, size, type } = file;
const fileRejection = fileRejections.find(
(fileRejection) => fileRejection.file === file
);
const { message } = fileRejection || {};
return (
<FileCard
key={name}
isInvalid={fileRejection != null}
name={name}
onRemove={handleRemove}
sizeInBytes={size}
type={type}
validationMessage={message}
/>
);
}}
values={files}
/>
</Pane>
);
}

1877
pages/spadki.js Normal file

File diff suppressed because it is too large Load Diff

367
pages/tracking.js Normal file
View File

@@ -0,0 +1,367 @@
import { useState, useEffect, useRef } from 'react';
import Head from 'next/head';
import { useRouter } from 'next/router';
import Layout from "../components/ui/Layout";
import { Card, CardHeader, CardContent, CardTitle } from "../components/ui/components";
export default function TrackingPage() {
const router = useRouter();
const [trackingNumber, setTrackingNumber] = useState('');
const [loading, setLoading] = useState(false);
const [error, setError] = useState('');
const [trackingData, setTrackingData] = useState(null);
// Use ref to keep track of if the component is mounted
const isMounted = useRef(true);
// Handle tracking number from URL query parameter
useEffect(() => {
if (router.query.number) {
setTrackingNumber(router.query.number);
fetchTrackingData(router.query.number);
}
return () => {
isMounted.current = false;
};
}, [router.query.number]);
// Copy text to clipboard function
const copyToClipboard = (text) => {
navigator.clipboard.writeText(text).catch(err => {
console.error('Failed to copy text: ', err);
});
};
// Format date to Polish locale
const formatDate = (dateString) => {
const date = new Date(dateString);
return date.toLocaleString('pl-PL', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit'
});
};
// Get country flag emoji
const getCountryFlag = (countryCode) => {
const flags = {
'PL': '🇵🇱',
'DE': '🇩🇪',
'FR': '🇫🇷',
'GB': '🇬🇧',
'US': '🇺🇸',
'IT': '🇮🇹',
'ES': '🇪🇸',
'NL': '🇳🇱',
'BE': '🇧🇪',
'CZ': '🇨🇿',
'SK': '🇸🇰',
'AT': '🇦🇹',
'CH': '🇨🇭'
};
return flags[countryCode] || '🌍';
};
// Validate tracking number format
const isValidTrackingNumber = (number) => {
return number && number.length >= 13 && /^[0-9A-Z]+$/.test(number.replace(/\s/g, ''));
};
// No longer needed - removed map-related function
// Handle form submission
const handleSubmit = (e) => {
e.preventDefault();
if (trackingNumber) {
fetchTrackingData(trackingNumber);
}
};
// Fetch tracking data from API
const fetchTrackingData = async (number) => {
if (!number) {
setError('Proszę wprowadzić numer przesyłki.');
return;
}
if (!isValidTrackingNumber(number)) {
setError('Nieprawidłowy format numeru przesyłki. Numer powinien zawierać tylko cyfry i litery.');
return;
}
setLoading(true);
setError('');
setTrackingData(null);
const apiKey =
"GI42fpjWMcTGI2oY0DAN4IulxR3QZy8+XhA2oPE17HB6P5JA+BTvuY5gIH0imCa2ZmH/vieaE66A/bRnSlsh3RIWVPHPtR5qVflsHx2mBtDAnzWFBmzZeDg8zVIPIPRN7TyM/SzqHDkt5IdfnRDRXKO5p/Bc/1Jpj0JpNKs+NTk=.RjcxMEEwRjI3NjA1QURGMjNCNzc4NTVGOTY0NkQ0RTA5NDQ4NTgxNzE2RTNEREFGMTkwNkU2QjlFRTlEMzEzRg==.443a0acd4c7d4417b55e1ae9e755d91a";
const url = `https://uss.poczta-polska.pl/uss/v2.0/tracking/checkmailex?language=PL&number=${number}&addPostOfficeInfo=true&events=true&states=true`;
try {
const response = await fetch(url, {
method: "GET",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
api_key: apiKey,
},
});
if (!response.ok) {
if (response.status === 404) {
throw new Error('Nie znaleziono przesyłki o podanym numerze.');
} else if (response.status === 403) {
throw new Error('Brak autoryzacji do API. Skontaktuj się z administratorem.');
} else {
throw new Error(`Błąd serwera: ${response.status}. Spróbuj ponownie później.`);
}
}
const data = await response.json();
if (!data.mailInfo) {
throw new Error('Brak danych o przesyłce. Sprawdź numer i spróbuj ponownie.');
}
if (isMounted.current) {
setTrackingData(data);
}
} catch (error) {
console.error("Error fetching tracking data:", error);
if (isMounted.current) {
setError(error.message || 'Wystąpił błąd podczas pobierania danych. Sprawdź połączenie internetowe i spróbuj ponownie.');
}
} finally {
if (isMounted.current) {
setLoading(false);
}
}
};
// Create a comprehensive timeline combining states and events
const getTimeline = (mailInfo) => {
if (!mailInfo) return [];
const timeline = [];
// Add states to timeline
if (mailInfo.states) {
mailInfo.states.forEach(state => {
timeline.push({
type: 'state',
data: state,
time: new Date(state.time)
});
});
}
// Add events to timeline
if (mailInfo.events) {
mailInfo.events.forEach(event => {
timeline.push({
type: 'event',
data: event,
time: new Date(event.time)
});
});
}
// Sort timeline by time (oldest first)
return timeline.sort((a, b) => a.time - b.time);
};
// Handle button click for copying tracking number
const handleCopy = () => {
if (trackingData?.mailInfo?.number) {
copyToClipboard(trackingData.mailInfo.number);
}
};
return (
<Layout title="Śledzenie przesyłek">
<div className="p-6 max-w-7xl mx-auto">
{/* Page Header */}
<div className="mb-8">
<h1 className="text-3xl font-bold text-gray-900 mb-2">
Śledzenie przesyłek
</h1>
<p className="text-gray-600">
Sprawdź status przesyłki
</p>
</div>
{/* Input Card */}
<Card className="mb-6">
<CardHeader className="border-b bg-gray-50">
<CardTitle>Wyszukiwanie przesyłki</CardTitle>
</CardHeader>
<CardContent className="p-6">
<form onSubmit={handleSubmit} className="flex flex-wrap gap-4">
<input
type="text"
value={trackingNumber}
onChange={(e) => setTrackingNumber(e.target.value)}
placeholder="Wprowadź numer przesyłki (np. 00659007734210841438)"
maxLength="50"
className="flex-1 min-w-[250px] block rounded-lg border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 transition-colors p-2.5 border"
/>
<button
type="submit"
disabled={loading}
className="bg-blue-600 hover:bg-blue-700 text-white font-medium py-2.5 px-5 rounded-lg transition-colors focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 disabled:bg-gray-400 disabled:cursor-not-allowed"
>
{loading ? 'Wyszukiwanie...' : 'Szukaj'}
</button>
</form>
</CardContent>
</Card>
{loading && (
<div className="flex items-center justify-center p-4 mb-6">
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600"></div>
<span className="ml-3 text-blue-600 font-medium">Pobieranie danych...</span>
</div>
)}
{error && (
<div className="bg-red-100 border-l-4 border-red-500 text-red-700 p-4 mb-6 rounded-lg">
<p>{error}</p>
</div>
)}
{trackingData && trackingData.mailInfo && (
<>
{/* Package Information Card */}
<Card className="mb-6">
<CardHeader className="border-b bg-gray-50">
<CardTitle>Dane przesyłki</CardTitle>
</CardHeader>
<CardContent className="p-6">
<div className="space-y-4">
<div className="flex flex-wrap items-center gap-2">
<span className="font-semibold text-gray-700 min-w-[140px]">Numer przesyłki:</span>
<span className="text-gray-900">{trackingData.mailInfo.number}</span>
<button
onClick={handleCopy}
className="ml-2 text-xs border border-blue-600 text-blue-600 hover:bg-blue-600 hover:text-white px-2 py-1 rounded transition-colors"
>
Kopiuj
</button>
</div>
<div className="flex flex-wrap items-center">
<span className="font-semibold text-gray-700 min-w-[140px]">Typ przesyłki:</span>
<span className="text-gray-900">{trackingData.mailInfo.typeOfMailName}</span>
</div>
<div className="flex flex-wrap items-center">
<span className="font-semibold text-gray-700 min-w-[140px]">Doręczono:</span>
<span className={`px-3 py-1 rounded-full text-sm font-medium ${
trackingData.mailInfo.finished
? 'bg-green-100 text-green-800'
: 'bg-yellow-100 text-yellow-800'
}`}>
{trackingData.mailInfo.finished ? "✓ Tak" : "⏳ Nie"}
</span>
</div>
<div className="flex flex-col sm:flex-row gap-4 mt-4 pt-4 border-t border-gray-200">
<div className="flex items-center gap-2 bg-gray-50 px-4 py-3 rounded-lg">
<span className="text-gray-700">🏁 Nadanie:</span>
<span className="font-medium">
{getCountryFlag(trackingData.mailInfo.dispatchCountryCode)} {trackingData.mailInfo.dispatchCountryName}
</span>
</div>
<div className="flex items-center gap-2 bg-gray-50 px-4 py-3 rounded-lg">
<span className="text-gray-700">🎯 Doręczenie:</span>
<span className="font-medium">
{getCountryFlag(trackingData.mailInfo.recipientCountryCode)} {trackingData.mailInfo.recipientCountryName}
</span>
</div>
</div>
</div>
</CardContent>
</Card>
{/* Package History Card */}
<Card>
<CardHeader className="border-b bg-gray-50">
<CardTitle>Historia przesyłki</CardTitle>
</CardHeader>
<CardContent className="p-6">
{getTimeline(trackingData.mailInfo).length > 0 ? (
<div className="relative pl-8 border-l-2 border-blue-500 ml-4">
{getTimeline(trackingData.mailInfo).map((item, index) => {
if (item.type === 'state') {
// Find related events for this state
const relatedEvents = trackingData.mailInfo.events ?
trackingData.mailInfo.events.filter(event =>
event.state && event.state.code === item.data.code
) : [];
return (
<div key={`state-${index}`} className="mb-8 relative">
<div className="absolute -left-[29px] w-6 h-6 bg-blue-600 rounded-full border-4 border-white"></div>
<div className="bg-white rounded-lg shadow-sm border border-gray-200 p-4 mb-3">
<div className="font-medium text-blue-700">📦 {item.data.name}</div>
<div className="text-sm text-gray-600 mt-1">🕒 {formatDate(item.data.time)}</div>
</div>
{relatedEvents.map((event, eventIndex) => (
<div key={`related-event-${eventIndex}`} className="ml-4 bg-blue-50 rounded-lg p-4 mb-3 border-l-4 border-blue-400">
<div className="font-medium text-blue-800">📋 {event.name}</div>
<div className="text-sm text-gray-600 mt-1">🕒 {formatDate(event.time)}</div>
{event.postOffice && event.postOffice.description && (
<div className="mt-2 text-sm bg-white p-2 rounded border border-gray-200">
📍 {event.postOffice.description.city}, {event.postOffice.description.street} {event.postOffice.description.houseNumber}
</div>
)}
</div>
))}
</div>
);
} else if (item.type === 'event') {
// Only show standalone events (not linked to states)
const isLinkedToState = trackingData.mailInfo.states &&
trackingData.mailInfo.states.some(state =>
item.data.state && item.data.state.code === state.code
);
if (!isLinkedToState) {
return (
<div key={`event-${index}`} className="mb-8 relative">
<div className="absolute -left-[29px] w-6 h-6 bg-gray-400 rounded-full border-4 border-white"></div>
<div className="bg-white rounded-lg shadow-sm border border-gray-200 p-4">
<div className="font-medium text-gray-700">📋 {item.data.name}</div>
<div className="text-sm text-gray-600 mt-1">🕒 {formatDate(item.data.time)}</div>
{item.data.postOffice && item.data.postOffice.description && (
<div className="mt-2 text-sm bg-gray-50 p-2 rounded border border-gray-200">
📍 {item.data.postOffice.description.city}, {item.data.postOffice.description.street} {item.data.postOffice.description.houseNumber}
</div>
)}
</div>
</div>
);
}
return null;
}
return null;
})}
</div>
) : (
<div className="text-center py-8 bg-gray-50 rounded-lg">
<p className="text-gray-600">Brak dostępnych informacji o historii przesyłki</p>
</div>
)}
</CardContent>
</Card>
</>
)}
</div>
</Layout>
);
}

545
pages/uziomy.js Normal file
View File

@@ -0,0 +1,545 @@
import { useState } from "react";
import { useSession, signIn } from "next-auth/react";
import Layout from "../components/ui/Layout";
import { Card, CardHeader, CardContent, CardTitle, CardDescription, Button, Input, Badge, Alert } from "../components/ui/components";
import { BoltIcon as LightningBoltIcon, CalculatorIcon, DocumentArrowDownIcon as DocumentDownloadIcon, ClipboardDocumentListIcon as ClipboardListIcon } from '@heroicons/react/24/outline';
import DatePicker from "react-datepicker";
import { registerLocale } from "react-datepicker";
import "react-datepicker/dist/react-datepicker.css";
import pl from "date-fns/locale/pl";
import PizZip from "pizzip";
import Docxtemplater from "docxtemplater";
registerLocale("pl", pl);
export default function Uziomy() {
const { data: session } = useSession();
const [currentStep, setCurrentStep] = useState(1);
const [isCalculating, setIsCalculating] = useState(false);
const [isGeneratingDoc, setIsGeneratingDoc] = useState(false);
const [isGeneratingDxf, setIsGeneratingDxf] = useState(false);
const [calDate, setCalDate] = useState(null);
const [ground, setGround] = useState({
wet_coef: 0,
resistivity: 0,
resistance: 0,
measure_dist: 0,
rod_len: 0,
rod_num: 0,
rod_coef: 0,
hor_len: 0,
result_v: 0,
result_h: 0,
result: 0,
wszrg_h: 0,
wszrg_v: 0,
wanted: 0,
date: undefined,
no: 0,
pr_title: "Budowa przyłącza kablowego nN",
in_city: undefined,
commune: undefined,
all_parcels: undefined,
target_parcel: undefined,
geo_data: undefined,
object: "Przyłącz kablowy nN",
objValue1: "proj.",
objName: undefined,
});
const [options] = useState([
{ label: "5 Ω", value: "5" },
{ label: "10 Ω", value: "10" },
{ label: "15 Ω", value: "15" },
{ label: "30 Ω", value: "30" },
]);
const [neededValue, setNeededValue] = useState("5");
const [resHValue, setResHValue] = useState("88");
const [resVValue, setResVValue] = useState("89");
const [objOptions1] = useState([
{ label: "proj.", value: "proj." },
{ label: "istn.", value: "istn." },
]);
const [objValue1, setObjValue1] = useState("proj.");
function getRandomInt(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min + 1)) + min;
}
function parseDate(dateString) {
const parts = dateString.split(".");
const day = parseInt(parts[0], 10);
const month = parseInt(parts[1], 10) - 1;
const year = parseInt(parts[2], 10);
return new Date(year, month, day);
}
function getGrounding(wanted, wszrg_h, wszrg_v, date) {
const dateObject = parseDate(date);
const month = dateObject.getMonth() + 1;
const wet_coef = month >= 6 && month <= 9 ? 1.2 : 1.6;
const rod_len = wanted === 30 ? 2 : 3;
const resistivity_h = wszrg_h / wet_coef;
const measure_dist_h = 1;
const resistance_h = resistivity_h / (2 * Math.PI * measure_dist_h);
const resistivity_v = wszrg_v / wet_coef;
const measure_dist_v = 1 + rod_len;
const resistance_v = resistivity_v / (2 * Math.PI * measure_dist_v);
const result_v = (wszrg_v / (2 * Math.PI * rod_len)) * (Math.log((8 * rod_len) / 0.016) - 1);
let rod_num = 2;
let hor_len = 1 + (rod_num - 1) * rod_len * 2;
let result_h = (wszrg_h / (2 * Math.PI * hor_len)) * Math.log((hor_len * hor_len) / (1 * 0.0191));
let rod_coef = Math.pow(rod_num, 4) * 0.00002 - Math.pow(rod_num, 3) * 0.0009 +
Math.pow(rod_num, 2) * 0.0137 - rod_num * 0.0981 + 1.0468;
let result = (result_v * result_h) / (result_v * rod_coef + rod_num * result_h * rod_coef);
while (result > wanted) {
rod_num += 1;
hor_len = 1 + (rod_num - 1) * rod_len * 2;
result_h = (wszrg_h / (2 * Math.PI * hor_len)) * Math.log((hor_len * hor_len) / (1 * 0.0191));
rod_coef = Math.pow(rod_num, 4) * 0.00002 - Math.pow(rod_num, 3) * 0.0009 +
Math.pow(rod_num, 2) * 0.0137 - rod_num * 0.0981 + 1.0468;
result = (result_v * result_h) / (result_v * rod_coef + rod_num * result_h * rod_coef);
}
return {
wet_coef: wet_coef,
resistivity_h: resistivity_h.toFixed(2),
resistance_h: resistance_h.toFixed(2),
measure_dist_h: measure_dist_h,
resistivity_v: resistivity_v.toFixed(2),
resistance_v: resistance_v.toFixed(2),
measure_dist_v: measure_dist_v,
rod_len: rod_len,
rod_num: rod_num,
rod_coef: rod_coef.toFixed(2),
hor_len: hor_len,
result_v: result_v.toFixed(2),
result_h: result_h.toFixed(2),
result: result.toFixed(2),
wszrg_h: wszrg_h,
wszrg_v: wszrg_v,
wanted: neededValue,
};
}
const calculateGrounding = () => {
setIsCalculating(true);
try {
const res = getGrounding(
parseInt(neededValue),
parseFloat(resHValue),
parseFloat(resVValue),
ground.date
);
setGround((current) => ({ ...current, ...res }));
} catch (error) {
alert("Błąd podczas obliczeń: " + error.message);
}
setIsCalculating(false);
};
const generateDocument = async () => {
setIsGeneratingDoc(true);
const data = {
...ground,
resisted_object: ground.objValue1 + " " + ground.objName,
};
try {
const response = await fetch("/api/generateDocx", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(data),
});
if (!response.ok) throw new Error("Response was not ok.");
const blob = await response.blob();
const downloadUrl = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = downloadUrl;
a.download = "opis_uziemienia.docx";
document.body.appendChild(a);
a.click();
a.remove();
} catch (error) {
console.error("Error:", error);
alert("Błąd podczas generowania dokumentu");
}
setIsGeneratingDoc(false);
};
const generateDxf = async () => {
setIsGeneratingDxf(true);
const dateParts = ground.date.split(".");
const month = dateParts[1];
const year = parseInt(dateParts[2], 10);
const formattedDate = month.padStart(2, "0") + "." + year;
const inputData = {
args: [
ground.objValue1 + ground.objName,
ground.pr_title,
ground.object,
ground.in_city + ", " + ground.commune + ", dz. nr " + ground.all_parcels,
formattedDate,
ground.hor_len,
ground.rod_len,
],
};
try {
const response = await fetch("/api/generateDxf", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(inputData),
});
if (!response.ok) throw new Error("Response was not ok.");
const blob = await response.blob();
const downloadUrl = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = downloadUrl;
a.download = "uziom.dxf";
document.body.appendChild(a);
a.click();
a.remove();
} catch (error) {
console.error("Error:", error);
alert("Błąd podczas generowania rysunku");
}
setIsGeneratingDxf(false);
};
if (session) {
return (
<Layout title="Wastpol - Kalkulator uziemień">
<div className="p-6 max-w-7xl mx-auto">
{/* Page Header */}
<div className="mb-8">
<div className="flex items-center space-x-3 mb-4">
<div className="p-3 bg-yellow-100 rounded-lg">
<LightningBoltIcon className="w-8 h-8 text-yellow-600" />
</div>
<div>
<h1 className="text-3xl font-bold text-gray-900">Kalkulator uziemień</h1>
<p className="text-gray-600">Oblicz parametry układu uziomowego i wygeneruj dokumentację</p>
</div>
</div>
</div>
<div className="grid grid-cols-1 lg:grid-cols-3 gap-8">
{/* Input Data Section */}
<div className="lg:col-span-2 space-y-6">
<Card>
<CardHeader>
<CardTitle className="flex items-center space-x-2">
<ClipboardListIcon className="w-5 h-5 text-blue-600" />
<span>Dane pomiarowe</span>
</CardTitle>
<CardDescription>
Wprowadź podstawowe dane pomiarowe i parametry projektu
</CardDescription>
</CardHeader>
<CardContent className="space-y-6">
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div className="space-y-4">
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Data wykonania pomiaru
</label>
<DatePicker
locale="pl"
selected={calDate}
onChange={(date) => {
setCalDate(date);
const day = date.getDate().toString().padStart(2, "0");
const month = (date.getMonth() + 1).toString().padStart(2, "0");
const year = date.getFullYear();
const formattedDate = `${day}.${month}.${year}`;
setGround((current) => ({ ...current, date: formattedDate }));
}}
placeholderText="Wybierz datę"
dateFormat="dd.MM.yyyy"
className="block w-full rounded-lg border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Wymagane uziemienie
</label>
<div className="space-y-2">
{options.map((option) => (
<label key={option.value} className="flex items-center">
<input
type="radio"
value={option.value}
checked={neededValue === option.value}
onChange={(e) => setNeededValue(e.target.value)}
className="mr-2"
/>
<span>{option.label}</span>
</label>
))}
</div>
</div>
<Input
label="Rezystywność gruntu pozioma (Ωm)"
placeholder="86"
value={resHValue}
onChange={(e) => {
setResHValue(e.target.value);
const val = parseInt(e.target.value);
const randomV = ground.wanted == 30 ?
getRandomInt(val - 10, val - 4) :
getRandomInt(val - 20, val - 10);
setResVValue(randomV.toString());
}}
/>
<Input
label="Numer protokołu pomiarowego"
placeholder="435"
value={ground.no}
onChange={(e) => setGround(curr => ({ ...curr, no: e.target.value }))}
/>
</div>
<div className="space-y-4">
<Input
label="Tytuł projektu"
placeholder="Budowa przyłącza kablowego nN"
value={ground.pr_title}
onChange={(e) => setGround(curr => ({ ...curr, pr_title: e.target.value }))}
/>
<Input
label="Obiekt"
placeholder="Przyłącz kablowy nN"
value={ground.object}
onChange={(e) => setGround(curr => ({ ...curr, object: e.target.value }))}
/>
<Input
label="Miejscowość"
placeholder="Paszyn"
value={ground.in_city}
onChange={(e) => setGround(curr => ({ ...curr, in_city: e.target.value }))}
/>
<Input
label="Gmina/Obręb"
placeholder="gm. Chełmiec"
value={ground.commune}
onChange={(e) => setGround(curr => ({ ...curr, commune: e.target.value }))}
/>
</div>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<Input
label="Wszystkie działki"
placeholder="423, 424, 425"
value={ground.all_parcels}
onChange={(e) => setGround(curr => ({ ...curr, all_parcels: e.target.value }))}
/>
<Input
label="Działka przyłączana"
placeholder="423"
value={ground.target_parcel}
onChange={(e) => setGround(curr => ({ ...curr, target_parcel: e.target.value }))}
/>
</div>
<Input
label="Współrzędne układu uziomowego"
placeholder="49°42'54&quot;N 20°38'0&quot;E"
value={ground.geo_data}
onChange={(e) => setGround(curr => ({ ...curr, geo_data: e.target.value }))}
/>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Typ obiektu
</label>
<div className="space-y-2">
{objOptions1.map((option) => (
<label key={option.value} className="flex items-center">
<input
type="radio"
value={option.value}
checked={objValue1 === option.value}
onChange={(e) => {
setObjValue1(e.target.value);
setGround(curr => ({ ...curr, objValue1: e.target.value }));
}}
className="mr-2"
/>
<span>{option.label}</span>
</label>
))}
</div>
</div>
<Input
label="Nazwa uziemianego obiektu"
placeholder="ZK2a-1P lub słup nr 54"
value={ground.objName}
onChange={(e) => setGround(curr => ({ ...curr, objName: e.target.value }))}
/>
</div>
</CardContent>
</Card>
</div>
{/* Results and Actions Section */}
<div className="space-y-6">
<Card>
<CardHeader>
<CardTitle className="flex items-center space-x-2">
<CalculatorIcon className="w-5 h-5 text-green-600" />
<span>Obliczenia</span>
</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<Button
onClick={calculateGrounding}
disabled={!ground.date || !resHValue || !resVValue || isCalculating}
loading={isCalculating}
className="w-full"
>
<CalculatorIcon className="w-5 h-5 mr-2" />
{isCalculating ? 'Obliczanie...' : 'Oblicz uziemienie'}
</Button>
{ground.result > 0 && (
<div className="space-y-3">
<Alert variant="success">
<div className="space-y-2">
<div className="flex justify-between">
<span>Uziemienie poziome:</span>
<Badge variant="info">{ground.result_h} Ω</Badge>
</div>
<div className="flex justify-between">
<span>Uziemienie pionowe:</span>
<Badge variant="info">{ground.result_v} Ω</Badge>
</div>
<div className="flex justify-between font-semibold">
<span>Uziemienie całkowite:</span>
<Badge variant="success">{ground.result} Ω</Badge>
</div>
<div className="flex justify-between">
<span>Liczba szpil:</span>
<Badge>{ground.rod_num} szt</Badge>
</div>
<div className="flex justify-between">
<span>Długość bednarki:</span>
<Badge>{ground.hor_len} m</Badge>
</div>
</div>
</Alert>
</div>
)}
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle className="flex items-center space-x-2">
<DocumentDownloadIcon className="w-5 h-5 text-purple-600" />
<span>Generuj dokumentację</span>
</CardTitle>
</CardHeader>
<CardContent className="space-y-3">
<Button
onClick={generateDocument}
disabled={ground.result <= 0 || isGeneratingDoc}
loading={isGeneratingDoc}
variant="outline"
className="w-full"
>
{isGeneratingDoc ? 'Generowanie...' : 'Generuj opis (DOCX)'}
</Button>
<Button
onClick={generateDxf}
disabled={ground.result <= 0 || isGeneratingDxf}
loading={isGeneratingDxf}
variant="outline"
className="w-full"
>
{isGeneratingDxf ? 'Generowanie...' : 'Generuj rysunek (DXF)'}
</Button>
</CardContent>
</Card>
{/* Instructions */}
<Card>
<CardHeader>
<CardTitle>Instrukcje</CardTitle>
</CardHeader>
<CardContent>
<div className="space-y-3 text-sm">
<div className="flex items-start space-x-3">
<div className="flex-shrink-0 w-6 h-6 bg-blue-100 rounded-full flex items-center justify-center">
<span className="text-xs font-semibold text-blue-600">1</span>
</div>
<p className="text-gray-600">Wprowadź dane pomiarowe i parametry projektu</p>
</div>
<div className="flex items-start space-x-3">
<div className="flex-shrink-0 w-6 h-6 bg-blue-100 rounded-full flex items-center justify-center">
<span className="text-xs font-semibold text-blue-600">2</span>
</div>
<p className="text-gray-600">Kliknij "Oblicz uziemienie" aby wykonać kalkulacje</p>
</div>
<div className="flex items-start space-x-3">
<div className="flex-shrink-0 w-6 h-6 bg-blue-100 rounded-full flex items-center justify-center">
<span className="text-xs font-semibold text-blue-600">3</span>
</div>
<p className="text-gray-600">Wygeneruj dokumentację - opis i rysunek techniczny</p>
</div>
</div>
</CardContent>
</Card>
</div>
</div>
</div>
</Layout>
);
}
return (
<div className="min-h-screen bg-gradient-to-br from-blue-50 to-indigo-100 flex items-center justify-center p-4">
<Card className="w-full max-w-md">
<CardHeader className="text-center">
<div className="mx-auto w-16 h-16 bg-blue-100 rounded-full flex items-center justify-center mb-4">
<img src="/logo.png" alt="Wastpol" className="w-10 h-10" />
</div>
<CardTitle className="text-2xl">Zaloguj się</CardTitle>
<p className="text-gray-600 mt-2">Uzyskaj dostęp do kalkulatora uziemień</p>
</CardHeader>
<CardContent>
<Button onClick={() => signIn()} className="w-full" size="lg">
Zaloguj się
</Button>
</CardContent>
</Card>
</div>
);
}

545
pages/uziomy_new.js Normal file
View File

@@ -0,0 +1,545 @@
import { useState } from "react";
import { useSession, signIn } from "next-auth/react";
import Layout from "../components/ui/Layout";
import { Card, CardHeader, CardContent, CardTitle, CardDescription, Button, Input, Badge, Alert } from "../components/ui/components";
import { BoltIcon as LightningBoltIcon, CalculatorIcon, DocumentArrowDownIcon as DocumentDownloadIcon, ClipboardDocumentListIcon as ClipboardListIcon } from '@heroicons/react/24/outline';
import DatePicker from "react-datepicker";
import { registerLocale } from "react-datepicker";
import "react-datepicker/dist/react-datepicker.css";
import pl from "date-fns/locale/pl";
import PizZip from "pizzip";
import Docxtemplater from "docxtemplater";
registerLocale("pl", pl);
export default function Uziomy() {
const { data: session } = useSession();
const [currentStep, setCurrentStep] = useState(1);
const [isCalculating, setIsCalculating] = useState(false);
const [isGeneratingDoc, setIsGeneratingDoc] = useState(false);
const [isGeneratingDxf, setIsGeneratingDxf] = useState(false);
const [calDate, setCalDate] = useState(null);
const [ground, setGround] = useState({
wet_coef: 0,
resistivity: 0,
resistance: 0,
measure_dist: 0,
rod_len: 0,
rod_num: 0,
rod_coef: 0,
hor_len: 0,
result_v: 0,
result_h: 0,
result: 0,
wszrg_h: 0,
wszrg_v: 0,
wanted: 0,
date: undefined,
no: 0,
pr_title: "Budowa przyłącza kablowego nN",
in_city: undefined,
commune: undefined,
all_parcels: undefined,
target_parcel: undefined,
geo_data: undefined,
object: "Przyłącz kablowy nN",
objValue1: "proj.",
objName: undefined,
});
const [options] = useState([
{ label: "5 Ω", value: "5" },
{ label: "10 Ω", value: "10" },
{ label: "15 Ω", value: "15" },
{ label: "30 Ω", value: "30" },
]);
const [neededValue, setNeededValue] = useState("5");
const [resHValue, setResHValue] = useState("88");
const [resVValue, setResVValue] = useState("89");
const [objOptions1] = useState([
{ label: "proj.", value: "proj." },
{ label: "istn.", value: "istn." },
]);
const [objValue1, setObjValue1] = useState("proj.");
function getRandomInt(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min + 1)) + min;
}
function parseDate(dateString) {
const parts = dateString.split(".");
const day = parseInt(parts[0], 10);
const month = parseInt(parts[1], 10) - 1;
const year = parseInt(parts[2], 10);
return new Date(year, month, day);
}
function getGrounding(wanted, wszrg_h, wszrg_v, date) {
const dateObject = parseDate(date);
const month = dateObject.getMonth() + 1;
const wet_coef = month >= 6 && month <= 9 ? 1.2 : 1.6;
const rod_len = wanted === 30 ? 2 : 3;
const resistivity_h = wszrg_h / wet_coef;
const measure_dist_h = 1;
const resistance_h = resistivity_h / (2 * Math.PI * measure_dist_h);
const resistivity_v = wszrg_v / wet_coef;
const measure_dist_v = 1 + rod_len;
const resistance_v = resistivity_v / (2 * Math.PI * measure_dist_v);
const result_v = (wszrg_v / (2 * Math.PI * rod_len)) * (Math.log((8 * rod_len) / 0.016) - 1);
let rod_num = 2;
let hor_len = 1 + (rod_num - 1) * rod_len * 2;
let result_h = (wszrg_h / (2 * Math.PI * hor_len)) * Math.log((hor_len * hor_len) / (1 * 0.0191));
let rod_coef = Math.pow(rod_num, 4) * 0.00002 - Math.pow(rod_num, 3) * 0.0009 +
Math.pow(rod_num, 2) * 0.0137 - rod_num * 0.0981 + 1.0468;
let result = (result_v * result_h) / (result_v * rod_coef + rod_num * result_h * rod_coef);
while (result > wanted) {
rod_num += 1;
hor_len = 1 + (rod_num - 1) * rod_len * 2;
result_h = (wszrg_h / (2 * Math.PI * hor_len)) * Math.log((hor_len * hor_len) / (1 * 0.0191));
rod_coef = Math.pow(rod_num, 4) * 0.00002 - Math.pow(rod_num, 3) * 0.0009 +
Math.pow(rod_num, 2) * 0.0137 - rod_num * 0.0981 + 1.0468;
result = (result_v * result_h) / (result_v * rod_coef + rod_num * result_h * rod_coef);
}
return {
wet_coef: wet_coef,
resistivity_h: resistivity_h.toFixed(2),
resistance_h: resistance_h.toFixed(2),
measure_dist_h: measure_dist_h,
resistivity_v: resistivity_v.toFixed(2),
resistance_v: resistance_v.toFixed(2),
measure_dist_v: measure_dist_v,
rod_len: rod_len,
rod_num: rod_num,
rod_coef: rod_coef.toFixed(2),
hor_len: hor_len,
result_v: result_v.toFixed(2),
result_h: result_h.toFixed(2),
result: result.toFixed(2),
wszrg_h: wszrg_h,
wszrg_v: wszrg_v,
wanted: neededValue,
};
}
const calculateGrounding = () => {
setIsCalculating(true);
try {
const res = getGrounding(
parseInt(neededValue),
parseFloat(resHValue),
parseFloat(resVValue),
ground.date
);
setGround((current) => ({ ...current, ...res }));
} catch (error) {
alert("Błąd podczas obliczeń: " + error.message);
}
setIsCalculating(false);
};
const generateDocument = async () => {
setIsGeneratingDoc(true);
const data = {
...ground,
resisted_object: ground.objValue1 + " " + ground.objName,
};
try {
const response = await fetch("/api/generateDocx", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(data),
});
if (!response.ok) throw new Error("Response was not ok.");
const blob = await response.blob();
const downloadUrl = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = downloadUrl;
a.download = "opis_uziemienia.docx";
document.body.appendChild(a);
a.click();
a.remove();
} catch (error) {
console.error("Error:", error);
alert("Błąd podczas generowania dokumentu");
}
setIsGeneratingDoc(false);
};
const generateDxf = async () => {
setIsGeneratingDxf(true);
const dateParts = ground.date.split(".");
const month = dateParts[1];
const year = parseInt(dateParts[2], 10);
const formattedDate = month.padStart(2, "0") + "." + year;
const inputData = {
args: [
ground.objValue1 + ground.objName,
ground.pr_title,
ground.object,
ground.in_city + ", " + ground.commune + ", dz. nr " + ground.all_parcels,
formattedDate,
ground.hor_len,
ground.rod_len,
],
};
try {
const response = await fetch("/api/generateDxf", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(inputData),
});
if (!response.ok) throw new Error("Response was not ok.");
const blob = await response.blob();
const downloadUrl = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = downloadUrl;
a.download = "uziom.dxf";
document.body.appendChild(a);
a.click();
a.remove();
} catch (error) {
console.error("Error:", error);
alert("Błąd podczas generowania rysunku");
}
setIsGeneratingDxf(false);
};
if (session) {
return (
<Layout title="Wastpol - Kalkulator uziemień">
<div className="p-6 max-w-7xl mx-auto">
{/* Page Header */}
<div className="mb-8">
<div className="flex items-center space-x-3 mb-4">
<div className="p-3 bg-yellow-100 rounded-lg">
<LightningBoltIcon className="w-8 h-8 text-yellow-600" />
</div>
<div>
<h1 className="text-3xl font-bold text-gray-900">Kalkulator uziemień</h1>
<p className="text-gray-600">Oblicz parametry układu uziomowego i wygeneruj dokumentację</p>
</div>
</div>
</div>
<div className="grid grid-cols-1 lg:grid-cols-3 gap-8">
{/* Input Data Section */}
<div className="lg:col-span-2 space-y-6">
<Card>
<CardHeader>
<CardTitle className="flex items-center space-x-2">
<ClipboardListIcon className="w-5 h-5 text-blue-600" />
<span>Dane pomiarowe</span>
</CardTitle>
<CardDescription>
Wprowadź podstawowe dane pomiarowe i parametry projektu
</CardDescription>
</CardHeader>
<CardContent className="space-y-6">
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div className="space-y-4">
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Data wykonania pomiaru
</label>
<DatePicker
locale="pl"
selected={calDate}
onChange={(date) => {
setCalDate(date);
const day = date.getDate().toString().padStart(2, "0");
const month = (date.getMonth() + 1).toString().padStart(2, "0");
const year = date.getFullYear();
const formattedDate = `${day}.${month}.${year}`;
setGround((current) => ({ ...current, date: formattedDate }));
}}
placeholderText="Wybierz datę"
dateFormat="dd.MM.yyyy"
className="block w-full rounded-lg border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Wymagane uziemienie
</label>
<div className="space-y-2">
{options.map((option) => (
<label key={option.value} className="flex items-center">
<input
type="radio"
value={option.value}
checked={neededValue === option.value}
onChange={(e) => setNeededValue(e.target.value)}
className="mr-2"
/>
<span>{option.label}</span>
</label>
))}
</div>
</div>
<Input
label="Rezystywność gruntu pozioma (Ωm)"
placeholder="86"
value={resHValue}
onChange={(e) => {
setResHValue(e.target.value);
const val = parseInt(e.target.value);
const randomV = ground.wanted == 30 ?
getRandomInt(val - 10, val - 4) :
getRandomInt(val - 20, val - 10);
setResVValue(randomV.toString());
}}
/>
<Input
label="Numer protokołu pomiarowego"
placeholder="435"
value={ground.no}
onChange={(e) => setGround(curr => ({ ...curr, no: e.target.value }))}
/>
</div>
<div className="space-y-4">
<Input
label="Tytuł projektu"
placeholder="Budowa przyłącza kablowego nN"
value={ground.pr_title}
onChange={(e) => setGround(curr => ({ ...curr, pr_title: e.target.value }))}
/>
<Input
label="Obiekt"
placeholder="Przyłącz kablowy nN"
value={ground.object}
onChange={(e) => setGround(curr => ({ ...curr, object: e.target.value }))}
/>
<Input
label="Miejscowość"
placeholder="Paszyn"
value={ground.in_city}
onChange={(e) => setGround(curr => ({ ...curr, in_city: e.target.value }))}
/>
<Input
label="Gmina/Obręb"
placeholder="gm. Chełmiec"
value={ground.commune}
onChange={(e) => setGround(curr => ({ ...curr, commune: e.target.value }))}
/>
</div>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<Input
label="Wszystkie działki"
placeholder="423, 424, 425"
value={ground.all_parcels}
onChange={(e) => setGround(curr => ({ ...curr, all_parcels: e.target.value }))}
/>
<Input
label="Działka przyłączana"
placeholder="423"
value={ground.target_parcel}
onChange={(e) => setGround(curr => ({ ...curr, target_parcel: e.target.value }))}
/>
</div>
<Input
label="Współrzędne układu uziomowego"
placeholder="49°42'54&quot;N 20°38'0&quot;E"
value={ground.geo_data}
onChange={(e) => setGround(curr => ({ ...curr, geo_data: e.target.value }))}
/>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Typ obiektu
</label>
<div className="space-y-2">
{objOptions1.map((option) => (
<label key={option.value} className="flex items-center">
<input
type="radio"
value={option.value}
checked={objValue1 === option.value}
onChange={(e) => {
setObjValue1(e.target.value);
setGround(curr => ({ ...curr, objValue1: e.target.value }));
}}
className="mr-2"
/>
<span>{option.label}</span>
</label>
))}
</div>
</div>
<Input
label="Nazwa uziemianego obiektu"
placeholder="ZK2a-1P lub słup nr 54"
value={ground.objName}
onChange={(e) => setGround(curr => ({ ...curr, objName: e.target.value }))}
/>
</div>
</CardContent>
</Card>
</div>
{/* Results and Actions Section */}
<div className="space-y-6">
<Card>
<CardHeader>
<CardTitle className="flex items-center space-x-2">
<CalculatorIcon className="w-5 h-5 text-green-600" />
<span>Obliczenia</span>
</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<Button
onClick={calculateGrounding}
disabled={!ground.date || !resHValue || !resVValue || isCalculating}
loading={isCalculating}
className="w-full"
>
<CalculatorIcon className="w-5 h-5 mr-2" />
{isCalculating ? 'Obliczanie...' : 'Oblicz uziemienie'}
</Button>
{ground.result > 0 && (
<div className="space-y-3">
<Alert variant="success">
<div className="space-y-2">
<div className="flex justify-between">
<span>Uziemienie poziome:</span>
<Badge variant="info">{ground.result_h} Ω</Badge>
</div>
<div className="flex justify-between">
<span>Uziemienie pionowe:</span>
<Badge variant="info">{ground.result_v} Ω</Badge>
</div>
<div className="flex justify-between font-semibold">
<span>Uziemienie całkowite:</span>
<Badge variant="success">{ground.result} Ω</Badge>
</div>
<div className="flex justify-between">
<span>Liczba szpil:</span>
<Badge>{ground.rod_num} szt</Badge>
</div>
<div className="flex justify-between">
<span>Długość bednarki:</span>
<Badge>{ground.hor_len} m</Badge>
</div>
</div>
</Alert>
</div>
)}
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle className="flex items-center space-x-2">
<DocumentDownloadIcon className="w-5 h-5 text-purple-600" />
<span>Generuj dokumentację</span>
</CardTitle>
</CardHeader>
<CardContent className="space-y-3">
<Button
onClick={generateDocument}
disabled={ground.result <= 0 || isGeneratingDoc}
loading={isGeneratingDoc}
variant="outline"
className="w-full"
>
{isGeneratingDoc ? 'Generowanie...' : 'Generuj opis (DOCX)'}
</Button>
<Button
onClick={generateDxf}
disabled={ground.result <= 0 || isGeneratingDxf}
loading={isGeneratingDxf}
variant="outline"
className="w-full"
>
{isGeneratingDxf ? 'Generowanie...' : 'Generuj rysunek (DXF)'}
</Button>
</CardContent>
</Card>
{/* Instructions */}
<Card>
<CardHeader>
<CardTitle>Instrukcje</CardTitle>
</CardHeader>
<CardContent>
<div className="space-y-3 text-sm">
<div className="flex items-start space-x-3">
<div className="flex-shrink-0 w-6 h-6 bg-blue-100 rounded-full flex items-center justify-center">
<span className="text-xs font-semibold text-blue-600">1</span>
</div>
<p className="text-gray-600">Wprowadź dane pomiarowe i parametry projektu</p>
</div>
<div className="flex items-start space-x-3">
<div className="flex-shrink-0 w-6 h-6 bg-blue-100 rounded-full flex items-center justify-center">
<span className="text-xs font-semibold text-blue-600">2</span>
</div>
<p className="text-gray-600">Kliknij "Oblicz uziemienie" aby wykonać kalkulacje</p>
</div>
<div className="flex items-start space-x-3">
<div className="flex-shrink-0 w-6 h-6 bg-blue-100 rounded-full flex items-center justify-center">
<span className="text-xs font-semibold text-blue-600">3</span>
</div>
<p className="text-gray-600">Wygeneruj dokumentację - opis i rysunek techniczny</p>
</div>
</div>
</CardContent>
</Card>
</div>
</div>
</div>
</Layout>
);
}
return (
<div className="min-h-screen bg-gradient-to-br from-blue-50 to-indigo-100 flex items-center justify-center p-4">
<Card className="w-full max-w-md">
<CardHeader className="text-center">
<div className="mx-auto w-16 h-16 bg-blue-100 rounded-full flex items-center justify-center mb-4">
<img src="/logo.png" alt="Wastpol" className="w-10 h-10" />
</div>
<CardTitle className="text-2xl">Zaloguj się</CardTitle>
<p className="text-gray-600 mt-2">Uzyskaj dostęp do kalkulatora uziemień</p>
</CardHeader>
<CardContent>
<Button onClick={() => signIn()} className="w-full" size="lg">
Zaloguj się
</Button>
</CardContent>
</Card>
</div>
);
}

View File

@@ -1,12 +1,16 @@
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap');
@tailwind base;
@tailwind components;
@tailwind utilities;
html,
body {
padding: 0;
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
font-family: 'Inter', -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
a {
@@ -17,3 +21,80 @@ a {
* {
box-sizing: border-box;
}
@layer base {
h1, h2, h3, h4, h5, h6 {
@apply font-semibold text-gray-900;
}
h1 {
@apply text-3xl lg:text-4xl;
}
h2 {
@apply text-2xl lg:text-3xl;
}
h3 {
@apply text-xl lg:text-2xl;
}
}
@layer components {
.btn-primary {
@apply bg-blue-600 hover:bg-blue-700 text-white font-medium py-2 px-4 rounded-lg transition-colors focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2;
}
.btn-secondary {
@apply bg-gray-100 hover:bg-gray-200 text-gray-900 font-medium py-2 px-4 rounded-lg transition-colors focus:outline-none focus:ring-2 focus:ring-gray-500 focus:ring-offset-2;
}
.card {
@apply bg-white rounded-xl shadow-sm border border-gray-200 hover:shadow-md transition-shadow;
}
.input-field {
@apply block w-full rounded-lg border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 transition-colors;
}
}
/* Custom scrollbar */
.scrollbar-thin {
scrollbar-width: thin;
scrollbar-color: #cbd5e1 #f1f5f9;
}
.scrollbar-thin::-webkit-scrollbar {
width: 6px;
}
.scrollbar-thin::-webkit-scrollbar-track {
background: #f1f5f9;
border-radius: 3px;
}
.scrollbar-thin::-webkit-scrollbar-thumb {
background: #cbd5e1;
border-radius: 3px;
}
.scrollbar-thin::-webkit-scrollbar-thumb:hover {
background: #94a3b8;
}
/* Loading animation */
@keyframes spin {
to {
transform: rotate(360deg);
}
}
.animate-spin {
animation: spin 1s linear infinite;
}
/* Canvas styling */
canvas {
border-radius: 8px;
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
}

View File

@@ -1,11 +1,38 @@
module.exports = {
content: [
"./pages/**/*.{js,ts,jsx,tsx}",
"./templates/**/*.{js,ts,jsx,tsx}",
"./components/**/*.{js,ts,jsx,tsx}",
],
theme: {
extend: {},
extend: {
colors: {
primary: {
50: '#eff6ff',
500: '#3b82f6',
600: '#2563eb',
700: '#1d4ed8',
},
},
fontFamily: {
sans: ['Inter', 'system-ui', 'sans-serif'],
},
animation: {
'fade-in': 'fadeIn 0.5s ease-in-out',
'slide-up': 'slideUp 0.3s ease-out',
},
keyframes: {
fadeIn: {
'0%': { opacity: '0' },
'100%': { opacity: '1' },
},
slideUp: {
'0%': { transform: 'translateY(10px)', opacity: '0' },
'100%': { transform: 'translateY(0)', opacity: '1' },
},
},
},
},
plugins: [],
plugins: [
require('@tailwindcss/forms'),
],
};

198
uziom.py Normal file
View File

@@ -0,0 +1,198 @@
import ezdxf
import sys
args = sys.argv[1:]
print(args)
object1 = args[0] #ZK2a-1P
object2 = args[1] #Budowa
object3 = args[2] #Przylacze
adres = args[3]
date = args[4]
len_h = int(args[5])
len_v = float(args[6])
a4_x = 31.5
a4_y = 44.55
# len_h = 25
# len_v = 3
rod_num = int(((len_h - 1)/(len_v*2)) + 1)
zk_width = 0.665
zk_depth = 0.32
zk_height = 1.64
start_x = ((a4_x - len_h) / 2) + zk_width
start_y = 28
overedge = 0.5
# date = "06.2023"
start_y2 = start_y - 7.5 #drugi uziom
doc = ezdxf.new(dxfversion="R2013", setup=False)
msp = doc.modelspace()
#doc.layers.add(name="tabele", color=0, linetype="SOLID")
#doc.layers.add(name="bednarka", color=1, linetype="SOLID")
#doc.layers.add(name="uziom", color=3, linetype="SOLID")
#doc.layers.add(name="zk", color=0, linetype="SOLID")
#doc.layers.add(name="opisy", color=0, linetype="SOLID")
#doc.layers.add(name="grunt", color=0, linetype="SOLID")
hatch = msp.add_hatch(color=3)
hatch_ground = msp.add_hatch(color=0)
hatch_gravel = msp.add_hatch(color=254)
doc.styles.new("Arial", dxfattribs={"font" : "Arial.ttf"})
#WYDURK
msp.add_line((0, 0), (0, a4_y), dxfattribs={"layer": "tabele", "color":0})
msp.add_line((0, a4_y), (a4_x, a4_y), dxfattribs={"layer": "tabele", "color":0})
msp.add_line((a4_x, a4_y), (a4_x, 0), dxfattribs={"layer": "tabele", "color":0})
msp.add_line((a4_x, 0), (0, 0), dxfattribs={"layer": "tabele", "color":0})
#UZIOM WIDOK Z Gory
msp.add_line((start_x, start_y), (start_x + len_h, start_y), dxfattribs={"layer": "bednarka", "color":1})
for rod in range(0,rod_num):
#kola i kreskowanie
msp.add_circle((start_x+1+(len_v*2*rod), start_y), 0.15, dxfattribs={"layer": "uziom", "color":3})
edge_path = hatch.paths.add_edge_path()
edge_path.add_arc((start_x+1+(len_v*2*rod), start_y), radius=0.15, start_angle=0, end_angle=360)
#wymiary
msp.add_line((start_x+1+(len_v*2*rod), start_y), (start_x+1+(len_v*2*rod), start_y+1), dxfattribs={"layer": "opisy"})
if rod == rod_num-1: continue
dim = msp.add_linear_dim(
base=(start_x+1+(len_v*2*rod), start_y+1), # location of the dimension line
p1=(start_x+1+(len_v*2*rod), start_y), # 1st measurement point
p2=(start_x+1+(len_v*2*(rod+1)), start_y), # 2nd measurement point
dimstyle="EZ_CURVED", # default dimension style
override={
# "dimtxsty": "Standard",
"dimtxt": 0.5,
"dimasz": 0.5,
},
text=str(len_v*2)+" m"
).render()
#wymiar 1. metra
dim = msp.add_linear_dim(
base=(start_x, start_y+1), # location of the dimension line
p1=(start_x, start_y), # 1st measurement point
p2=(start_x+1, start_y), # 2nd measurement point
dimstyle="EZ_CURVED", # default dimension style
override={
# "dimtxsty": "Standard",
"dimtxt": 0.5,
"dimasz": 0.5,
},
text="1 m"
).render()
#ZK WIDOK Z Gory
msp.add_line((start_x-zk_width, start_y+zk_depth/2), (start_x, start_y+zk_depth/2), dxfattribs={"layer": "zk"})
msp.add_line((start_x, start_y+zk_depth/2), (start_x, start_y-zk_depth/2), dxfattribs={"layer": "zk"})
msp.add_line((start_x, start_y-zk_depth/2), (start_x-zk_width, start_y-zk_depth/2), dxfattribs={"layer": "zk"})
msp.add_line((start_x-zk_width, start_y-zk_depth/2), (start_x-zk_width, start_y+zk_depth/2), dxfattribs={"layer": "zk"})
#UZIOM WIDOK Z BOKU
msp.add_line((start_x - zk_width / 2, start_y2), (start_x + len_h, start_y2), dxfattribs={"layer": "bednarka", "color":1})
msp.add_line((start_x - zk_width / 2, start_y2), (start_x - zk_width / 2, start_y2+0.2), dxfattribs={"layer": "bednarka", "color":1})
for rod in range(0,rod_num):
msp.add_line((start_x+1+(rod*len_v*2), start_y2+0.02), (start_x+1+(rod*len_v*2), start_y2-len_v), dxfattribs={"layer": "uziom", "color":3})
msp.add_line((start_x+1+(rod*len_v*2), start_y2+0.02), (start_x+1+(rod*len_v*2)+0.016, start_y2+0.02), dxfattribs={"layer": "uziom", "color":3})
msp.add_line((start_x+1+(rod*len_v*2)+0.016, start_y2+0.02), (start_x+1+(rod*len_v*2)+0.016, start_y2-len_v), dxfattribs={"layer": "uziom", "color":3})
msp.add_line((start_x+1+(rod*len_v*2), start_y2-len_v), (start_x+1+(rod*len_v*2)+0.016, start_y2-len_v), dxfattribs={"layer": "uziom", "color":3})
dim = msp.add_linear_dim(
base=(start_x+0.5, start_y2+1), # location of the dimension line
p1=(start_x+0.5, start_y2+1), # 1st measurement point
p2=(start_x+0.5, start_y2), # 2nd measurement point
dimstyle="EZ_CURVED", # default dimension style
override={
# "dimtxsty": "Standard",
"dimtxt": 0.5,
"dimasz": 0.5,
"dimjust": 3,
},
text="h=1 m",
angle=-90
).render()
#PODZIEMIE WIDOK Z BOKU
msp.add_line((start_x, start_y2 + 1), (start_x + len_h + overedge, start_y2 + 1), dxfattribs={"layer": "grunt"})
hatch_ground.set_pattern_fill("EARTH2", scale=0.1)
hatch_ground.paths.add_polyline_path(
[(start_x, start_y2 + 1), (start_x + len_h + overedge, start_y2 + 1), (start_x + len_h + overedge, start_y2 + 0.5), (start_x, start_y2 + 0.5)], is_closed=True
)
hatch_gravel.set_pattern_fill("CONCRETE2", scale=0.1)
hatch_gravel.paths.add_polyline_path(
[(start_x, start_y2 + 1), (start_x + len_h + overedge, start_y2 + 1), (start_x + len_h + overedge, start_y2 -(len_v+1)), (start_x, start_y2 -(len_v+1))], is_closed=True
)
#ZK WIDOK Z BOKU
msp.add_line((start_x - zk_width, start_y2+(zk_height/2)+1), (start_x, start_y2+(zk_height/2)+1), dxfattribs={"layer": "zk"})
msp.add_line((start_x, start_y2+(zk_height/2)+1), (start_x, start_y2-(zk_height/2)+1), dxfattribs={"layer": "zk"})
msp.add_line((start_x, start_y2-(zk_height/2)+1), (start_x-zk_width, start_y2-(zk_height/2)+1), dxfattribs={"layer": "zk"})
msp.add_line((start_x-zk_width, start_y2-(zk_height/2)+1), (start_x - zk_width, start_y2+(zk_height/2)+1), dxfattribs={"layer": "zk"})
msp.add_text(object1, dxfattribs={"layer": "opisy", "height": 0.55, "rotation": 0, "style": "OpenSans"}).set_pos((start_x - (zk_width/2) , start_y2+2.3), align="CENTER")
#OPISY
msp.add_text("SCHEMAT ROZMIESZCZENIA UZIOMÓW", dxfattribs={"layer": "opisy", "height": 0.55, "rotation": 0, "style": "OpenSans"}).set_pos((a4_x/2, a4_y-5.5), align="CENTER")
#TABELKA
start_xt = a4_x - 1.5 - 24
start_yt = 2
points = [(start_xt, start_yt), (start_xt + 24, start_yt), (start_xt + 24, start_yt+4), (start_xt, start_yt+4), (start_xt, start_yt)]
msp.add_lwpolyline(points, dxfattribs={"layer": "opisy"})
msp.add_line((start_xt, start_yt+1), (start_xt + 16, start_yt+1), dxfattribs={"layer": "opisy"})
msp.add_text("RYSUNEK", dxfattribs={"layer": "opisy", "height": 0.25, "rotation": 0, "style": "Arial"}).set_pos((start_xt+1.5, start_yt+1+0.17), align="CENTER")
msp.add_text("Konfiguracja układu uziomowego", dxfattribs={"layer": "opisy", "height": 0.25, "rotation": 0, "style": "Arial"}).set_pos((start_xt+3+0.18, start_yt+1+0.17), align="LEFT")
msp.add_line((start_xt, start_yt+1.6), (start_xt + 16, start_yt+1.6), dxfattribs={"layer": "opisy"})
msp.add_text("TEMAT", dxfattribs={"layer": "opisy", "height": 0.25, "rotation": 0, "style": "Arial"}).set_pos((start_xt+1.5, start_yt+1.6+0.17), align="CENTER")
msp.add_text(object2, dxfattribs={"layer": "opisy", "height": 0.25, "rotation": 0, "style": "Arial"}).set_pos((start_xt+3+0.18, start_yt+1.6+0.17), align="LEFT")
msp.add_line((start_xt, start_yt+2.2), (start_xt + 16, start_yt+2.2), dxfattribs={"layer": "opisy"})
msp.add_text("ADRES", dxfattribs={"layer": "opisy", "height": 0.25, "rotation": 0, "style": "Arial"}).set_pos((start_xt+1.5, start_yt+2.2+0.17), align="CENTER")
msp.add_text(adres, dxfattribs={"layer": "opisy", "height": 0.25, "rotation": 0, "style": "Arial"}).set_pos((start_xt+3+0.18, start_yt+2.2+0.17), align="LEFT")
msp.add_line((start_xt, start_yt+2.8), (start_xt + 16, start_yt+2.8), dxfattribs={"layer": "opisy"})
msp.add_text("OBIEKT", dxfattribs={"layer": "opisy", "height": 0.25, "rotation": 0, "style": "Arial"}).set_pos((start_xt+1.5, start_yt+2.8+0.17), align="CENTER")
msp.add_text(object3, dxfattribs={"layer": "opisy", "height": 0.25, "rotation": 0, "style": "Arial"}).set_pos((start_xt+3+0.18, start_yt+2.8+0.17), align="LEFT")
msp.add_line((start_xt, start_yt+3.4), (start_xt + 16, start_yt+3.4), dxfattribs={"layer": "opisy"})
msp.add_text("INWESTOR", dxfattribs={"layer": "opisy", "height": 0.25, "rotation": 0, "style": "Arial"}).set_pos((start_xt+1.5, start_yt+3.4+0.17), align="CENTER")
msp.add_text("TAURON Dystrybucja S.A.", dxfattribs={"layer": "opisy", "height": 0.25, "rotation": 0, "style": "Arial"}).set_pos((start_xt+3+0.18, start_yt+3.4+0.17), align="LEFT")
msp.add_line((start_xt+3, start_yt), (start_xt + 3, start_yt+4), dxfattribs={"layer": "opisy"})
msp.add_line((start_xt+16, start_yt), (start_xt + 16, start_yt+4), dxfattribs={"layer": "opisy"})
msp.add_line((start_xt+3, start_yt+0.5), (start_xt + 16, start_yt+0.5), dxfattribs={"layer": "opisy"})
msp.add_text("STADIUM", dxfattribs={"layer": "opisy", "height": 0.25, "rotation": 0, "style": "Arial"}).set_pos((start_xt+3+1.625, start_yt+0.5+0.15), align="CENTER")
msp.add_text("PB", dxfattribs={"layer": "opisy", "height": 0.25, "rotation": 0, "style": "Arial"}).set_pos((start_xt+3+1.625, start_yt+0.15), align="CENTER")
msp.add_line((start_xt+3+3.25, start_yt), (start_xt + 3 + 3.25, start_yt+1), dxfattribs={"layer": "opisy"})
msp.add_text("DATA", dxfattribs={"layer": "opisy", "height": 0.25, "rotation": 0, "style": "Arial"}).set_pos((start_xt+3+3.25+1.625, start_yt+0.5+0.15), align="CENTER")
msp.add_text(date, dxfattribs={"layer": "opisy", "height": 0.25, "rotation": 0, "style": "Arial"}).set_pos((start_xt+3+3.25+1.625, start_yt+0.15), align="CENTER")
msp.add_line((start_xt+3+6.5, start_yt), (start_xt + 3 + 6.5, start_yt+1), dxfattribs={"layer": "opisy"})
msp.add_text("SKALA", dxfattribs={"layer": "opisy", "height": 0.25, "rotation": 0, "style": "Arial"}).set_pos((start_xt+3+6.5+1.625, start_yt+0.5+0.15), align="CENTER")
msp.add_text("1:150", dxfattribs={"layer": "opisy", "height": 0.25, "rotation": 0, "style": "Arial"}).set_pos((start_xt+3+6.5+1.625, start_yt+0.15), align="CENTER")
msp.add_line((start_xt+3+9.75, start_yt), (start_xt + 3 + 9.75, start_yt+1), dxfattribs={"layer": "opisy"})
msp.add_text("NR RYS", dxfattribs={"layer": "opisy", "height": 0.25, "rotation": 0, "style": "Arial"}).set_pos((start_xt+3+9.75+1.625, start_yt+0.5+0.15), align="CENTER")
msp.add_text("5", dxfattribs={"layer": "opisy", "height": 0.25, "rotation": 0, "style": "Arial"}).set_pos((start_xt+3+9.75+1.625, start_yt+0.15), align="CENTER")
msp.add_text("pręt uziemiający Ø16 - dł. "+ str(len_v) +" mb - "+ str(rod_num) +" szt.", dxfattribs={"layer": "opisy", "height": 0.25, "rotation": 0, "style": "Arial"}).set_pos((start_xt+2.5, start_yt+4+0.7), align="LEFT")
msp.add_circle((start_xt+1.5, start_yt + 4.8), 0.15, dxfattribs={"layer": "uziom", "color":3})
edge_path = hatch.paths.add_edge_path()
edge_path.add_arc((start_xt+1.5, start_yt + 4.8), radius=0.15, start_angle=0, end_angle=360)
msp.add_text("płaskownik St/Zn 30x4 mm - dł. "+ str(len_h) +" mb", dxfattribs={"layer": "opisy", "height": 0.25, "rotation": 0, "style": "Arial"}).set_pos((start_xt+2.5, start_yt+4+1.4), align="LEFT")
msp.add_line((start_xt +0.8, start_yt+4+1.5), (start_xt + 2.1, start_yt+4+1.5), dxfattribs={"layer": "bednarka", "color":1})
doc.saveas("public/uziom.dxf")

BIN
wet_input_15.docx Normal file

Binary file not shown.

BIN
wet_input_3.docx Normal file

Binary file not shown.