Initial commit

This commit is contained in:
dk
2025-12-29 13:05:50 +02:00
commit ee163b0c18
83 changed files with 1927 additions and 0 deletions

1
.env.production Normal file
View File

@@ -0,0 +1 @@
DISABLE_ESLINT_PLUGIN=true

View File

@@ -0,0 +1,83 @@
name: Build
on:
workflow_dispatch:
inputs:
branch:
description: 'Branch to build'
required: true
default: 'main'
permissions:
contents: read
jobs:
build:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Checkout branch
uses: actions/checkout@v3
with:
ref: ${{ gitea.event.inputs.branch }}
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: 24
- name: Cache node_modules
id: cache-node-modules
uses: actions/cache@v3
continue-on-error: true
with:
path: node_modules
key: ${{ runner.os }}-node-modules-${{ hashFiles('package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-modules-
- name: Install dependencies
run: |
set -euo pipefail
if [ -d node_modules ] && [ "${{ steps.cache-node-modules.outputs.cache-hit }}" == "true" ]; then
echo "Cache hit, verifying dependencies..."
npm ci --prefer-offline --no-audit --silent 2>&1 | tee install.log || npm install --no-audit --silent 2>&1 | tee install.log
else
echo "Cache miss, installing dependencies..."
npm ci --prefer-offline --no-audit --silent 2>&1 | tee install.log
fi
- name: Build (react-scripts build)
env:
CI: 'false'
run: |
set -euo pipefail
npm run build 2>&1 | tee build.log
timeout-minutes: 5
- name: Save node_modules cache
if: always()
uses: actions/cache@v3
continue-on-error: true
with:
path: node_modules
key: ${{ runner.os }}-node-modules-${{ hashFiles('package-lock.json') }}
- name: Verify build folder exists
run: test -d build || (echo "No build folder. Check build logs above."; exit 1)
- name: Upload logs (always)
if: always()
uses: actions/upload-artifact@v3
with:
name: build-logs
path: |
install.log
build.log
npm-debug.log*
if-no-files-found: ignore
- name: Build completed
if: success()
run: echo "Build completed successfully"

41
package.json Normal file
View File

@@ -0,0 +1,41 @@
{
"name": "zpep-website",
"version": "0.1.0",
"private": true,
"dependencies": {
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-scripts": "^5.0.1",
"framer-motion": "^11.0.0",
"lucide-react": "^0.400.0"
},
"devDependencies": {
"tailwindcss": "^3.4.0",
"postcss": "^8.4.0",
"autoprefixer": "^10.4.0"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}

6
postcss.config.js Normal file
View File

@@ -0,0 +1,6 @@
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {}
}
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
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

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
R0lGODlhAQAIAKIAAN7n89Ha49Td6MjR2s3W4Nzl8ODq9djh6yH5BAAAAAAALAAAAAABAAgAAAMGaBRyBSMBADs=

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAABN2lDQ1BBZG9iZSBSR0IgKDE5OTgpAAAokZWPv0rDUBSHvxtFxaFWCOLgcCdRUGzVwYxJW4ogWKtDkq1JQ5ViEm6uf/oQjm4dXNx9AidHwUHxCXwDxamDQ4QMBYvf9J3fORzOAaNi152GUYbzWKt205Gu58vZF2aYAoBOmKV2q3UAECdxxBjf7wiA10277jTG+38yH6ZKAyNguxtlIYgK0L/SqQYxBMygn2oQD4CpTto1EE9AqZf7G1AKcv8ASsr1fBBfgNlzPR+MOcAMcl8BTB1da4Bakg7UWe9Uy6plWdLuJkEkjweZjs4zuR+HiUoT1dFRF8jvA2AxH2w3HblWtay99X/+PRHX82Vun0cIQCw9F1lBeKEuf1UYO5PrYsdwGQ7vYXpUZLs3cLcBC7dFtlqF8hY8Dn8AwMZP/fNTP8gAAAAJcEhZcwAACxMAAAsTAQCanBgAAAaxaVRYdFhNTDpjb20uYWRvYmUueG1wAAAAAAA8P3hwYWNrZXQgYmVnaW49Iu+7vyIgaWQ9Ilc1TTBNcENlaGlIenJlU3pOVGN6a2M5ZCI/PiA8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJBZG9iZSBYTVAgQ29yZSA1LjYtYzE0OCA3OS4xNjQwMzYsIDIwMTkvMDgvMTMtMDE6MDY6NTcgICAgICAgICI+IDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+IDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczpzdEV2dD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlRXZlbnQjIiB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iIHhtbG5zOnBob3Rvc2hvcD0iaHR0cDovL25zLmFkb2JlLmNvbS9waG90b3Nob3AvMS4wLyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ1M1IFdpbmRvd3MiIHhtcDpDcmVhdGVEYXRlPSIyMDIwLTAyLTA3VDEyOjAyOjMyKzAyOjAwIiB4bXA6TW9kaWZ5RGF0ZT0iMjAyMC0wMi0wOVQxNTozMjoyNCswMjowMCIgeG1wOk1ldGFkYXRhRGF0ZT0iMjAyMC0wMi0wOVQxNTozMjoyNCswMjowMCIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDpiNTBiMGViNy00ODQ2LWE3NDktYTg0Zi0wNzc2Yjc0MTFiOWIiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6RTg3RTcxMTBEN0EzMTFFNDlENzVEQTE4NDg1Mjg2MUEiIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDpFODdFNzExMEQ3QTMxMUU0OUQ3NURBMTg0ODUyODYxQSIgZGM6Zm9ybWF0PSJpbWFnZS9wbmciIHBob3Rvc2hvcDpDb2xvck1vZGU9IjMiIHBob3Rvc2hvcDpJQ0NQcm9maWxlPSJBZG9iZSBSR0IgKDE5OTgpIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6RTg3RTcxMEREN0EzMTFFNDlENzVEQTE4NDg1Mjg2MUEiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6RTg3RTcxMEVEN0EzMTFFNDlENzVEQTE4NDg1Mjg2MUEiLz4gPHhtcE1NOkhpc3Rvcnk+IDxyZGY6U2VxPiA8cmRmOmxpIHN0RXZ0OmFjdGlvbj0ic2F2ZWQiIHN0RXZ0Omluc3RhbmNlSUQ9InhtcC5paWQ6MWNjNWI3NTItMDEwYS1mNjRiLTk5YWMtMzJiMTM3Y2RmMGY3IiBzdEV2dDp3aGVuPSIyMDIwLTAyLTA5VDEzOjQ5OjM2KzAyOjAwIiBzdEV2dDpzb2Z0d2FyZUFnZW50PSJBZG9iZSBQaG90b3Nob3AgMjEuMCAoV2luZG93cykiIHN0RXZ0OmNoYW5nZWQ9Ii8iLz4gPHJkZjpsaSBzdEV2dDphY3Rpb249InNhdmVkIiBzdEV2dDppbnN0YW5jZUlEPSJ4bXAuaWlkOmI1MGIwZWI3LTQ4NDYtYTc0OS1hODRmLTA3NzZiNzQxMWI5YiIgc3RFdnQ6d2hlbj0iMjAyMC0wMi0wOVQxNTozMjoyNCswMjowMCIgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWRvYmUgUGhvdG9zaG9wIDIxLjAgKFdpbmRvd3MpIiBzdEV2dDpjaGFuZ2VkPSIvIi8+IDwvcmRmOlNlcT4gPC94bXBNTTpIaXN0b3J5PiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/Pn//w8kAAAF5SURBVDiN1dQ/axRBGMfxz96ZRBQxMY1gZafgmxC0CjaGYLq0N9jY2ArWWgp7nWII2Ci+hxQWIpgupBDtIkkMKCLhWM9inzvmNncYwjV5YGCe2We/83v+MEW/3zdNa02VdiaA55oH3bJsozrJz52Ujp0VeVO6ZVlgcDCHKzgffoUj/InVGwdtKpxFwi1cw3wAq+yinzjELra6Zfmqk1JvEvAunqMd/j6e4Ff4l/EUi+H38BkfB4BmU3oZ7Ajv8Re3YxVYx++ImcF8lGpUYbcsYRsboegtlvAMFyNsFa9xD/dxFXtZOY6l/A1rEbCEFVzKvl/AA3zAo8hmZCJaY/Yt3MELdWOathCql6MEVd7p5tjADbzDzTGw3L5HFp/Q7qRUNRXmgZv/gVGnfRD7oaoRYEg/xGO8xI66UfnawRs8xFd1HYfAYtzzFakv4rp6bHJr4Qt+xL7fSWlil4cXRToHE74PwCOwiQpD5WDAmw9F0YScCHham/p7+A+BcXPdDcBb1QAAAABJRU5ErkJggg==

View File

@@ -0,0 +1 @@
iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAABN2lDQ1BBZG9iZSBSR0IgKDE5OTgpAAAokZWPv0rDUBSHvxtFxaFWCOLgcCdRUGzVwYxJW4ogWKtDkq1JQ5ViEm6uf/oQjm4dXNx9AidHwUHxCXwDxamDQ4QMBYvf9J3fORzOAaNi152GUYbzWKt205Gu58vZF2aYAoBOmKV2q3UAECdxxBjf7wiA10277jTG+38yH6ZKAyNguxtlIYgK0L/SqQYxBMygn2oQD4CpTto1EE9AqZf7G1AKcv8ASsr1fBBfgNlzPR+MOcAMcl8BTB1da4Bakg7UWe9Uy6plWdLuJkEkjweZjs4zuR+HiUoT1dFRF8jvA2AxH2w3HblWtay99X/+PRHX82Vun0cIQCw9F1lBeKEuf1UYO5PrYsdwGQ7vYXpUZLs3cLcBC7dFtlqF8hY8Dn8AwMZP/fNTP8gAAAAJcEhZcwAACxMAAAsTAQCanBgAAAaxaVRYdFhNTDpjb20uYWRvYmUueG1wAAAAAAA8P3hwYWNrZXQgYmVnaW49Iu+7vyIgaWQ9Ilc1TTBNcENlaGlIenJlU3pOVGN6a2M5ZCI/PiA8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJBZG9iZSBYTVAgQ29yZSA1LjYtYzE0OCA3OS4xNjQwMzYsIDIwMTkvMDgvMTMtMDE6MDY6NTcgICAgICAgICI+IDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+IDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczpzdEV2dD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlRXZlbnQjIiB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iIHhtbG5zOnBob3Rvc2hvcD0iaHR0cDovL25zLmFkb2JlLmNvbS9waG90b3Nob3AvMS4wLyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ1M1IFdpbmRvd3MiIHhtcDpDcmVhdGVEYXRlPSIyMDIwLTAyLTA3VDEyOjAyOjMyKzAyOjAwIiB4bXA6TW9kaWZ5RGF0ZT0iMjAyMC0wMi0wOVQxNToyODoyNyswMjowMCIgeG1wOk1ldGFkYXRhRGF0ZT0iMjAyMC0wMi0wOVQxNToyODoyNyswMjowMCIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDpmY2NlOGJlMC02NGE4LWVhNDYtOTc2OC03MjllYWE5ZjRjMTQiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6RUU5RDM1QkNEN0EzMTFFNDk4NzlBMUU2MzczNTY0MDciIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDpFRTlEMzVCQ0Q3QTMxMUU0OTg3OUExRTYzNzM1NjQwNyIgZGM6Zm9ybWF0PSJpbWFnZS9wbmciIHBob3Rvc2hvcDpDb2xvck1vZGU9IjMiIHBob3Rvc2hvcDpJQ0NQcm9maWxlPSJBZG9iZSBSR0IgKDE5OTgpIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6RUU5RDM1QjlEN0EzMTFFNDk4NzlBMUU2MzczNTY0MDciIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6RUU5RDM1QkFEN0EzMTFFNDk4NzlBMUU2MzczNTY0MDciLz4gPHhtcE1NOkhpc3Rvcnk+IDxyZGY6U2VxPiA8cmRmOmxpIHN0RXZ0OmFjdGlvbj0ic2F2ZWQiIHN0RXZ0Omluc3RhbmNlSUQ9InhtcC5paWQ6MzFmMzdlMjUtNjk5My1iMDQ5LWE5YWItYWZiMzljZWRiZmNmIiBzdEV2dDp3aGVuPSIyMDIwLTAyLTA5VDEzOjUxOjM2KzAyOjAwIiBzdEV2dDpzb2Z0d2FyZUFnZW50PSJBZG9iZSBQaG90b3Nob3AgMjEuMCAoV2luZG93cykiIHN0RXZ0OmNoYW5nZWQ9Ii8iLz4gPHJkZjpsaSBzdEV2dDphY3Rpb249InNhdmVkIiBzdEV2dDppbnN0YW5jZUlEPSJ4bXAuaWlkOmZjY2U4YmUwLTY0YTgtZWE0Ni05NzY4LTcyOWVhYTlmNGMxNCIgc3RFdnQ6d2hlbj0iMjAyMC0wMi0wOVQxNToyODoyNyswMjowMCIgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWRvYmUgUGhvdG9zaG9wIDIxLjAgKFdpbmRvd3MpIiBzdEV2dDpjaGFuZ2VkPSIvIi8+IDwvcmRmOlNlcT4gPC94bXBNTTpIaXN0b3J5PiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PujtqZUAAAOmSURBVDiNrdRPaFxFHAfw7/x5M/Oy773dsN0ki4hePOrFqwpiMQeFIgiCp5hcbNnYCsWDgrUokoDYEluaxlSMFUtE8NDqrbl5Ew+CeBFLm3Rjdtl/9u17++bNzPOgDdEk9OIP5jAz/D78ZpjfkKIo8H8G3zu5jxdFAUIICCG7e19evcqdc6Ex5s/ZuTn7QLAoit2xfOkSKYpCMsacUiovBUERhqGx1mZpmjIAh4L0PmatRZ7nuHjhAjHGSGPMyBjjATjFOd9QSh0dK5US3/f1F2trhx6ZAoC1Fmma4vy5c3SYJIXW2gCYAbBhrf3QGvOsK4q3GaVcKgUpJT67cuVw0BiDXq+Hbq9He93uE5nW3wghXvU8bzWO4812u73W63Yfy7LsNCUEUkoIIfDpysoDwE7HdDqd9VGaHuOcP0opPZam6ZnNzc2Z32/dOt7r9U5ba+ueEFBKgXOOy8vLB99hlmUYJgkGg8EnSZIAwFdSylNhGF5zzqHdat1otVo/aa3Pccbg+z6UUmCM7Qc55/B9H5wxOOe+1VpvEkJ+LZVKv1WrVVSrVRBKEcfxy1rrpyilR4WU8H0fnucdDN64fh1RFEEqtZ0b84HW+n1KaSilRBRFCIMAlNLHnXMVQsibHudc/VPl6urqwRWOj48jDAKYPL+2s7MTDwaDM2maglI6FYbhku/73zvn7jjnniaUvig8D77vQ+ypkgPA5eVl4nkegiAokjRFt9O5d7fZfMdY+/lErdYTQsz5vv8wgJvW2rta62eEEGcZYzeFlPeEELsg2dtuS0tL6Pf72G420e12AUIuHqlWX6/X6z9WKpUdzrkvhMjL5XIclcvPc87fyvN8ZXp6+t9HBgBCCBqNBoJSCVEUgTGGJEne3dra2m5ub/8RD4eiKAptrc3S0cgM41inSfKR1hpfr6+TfSAAUErRmJ9HpVJBuVwGgI4rivfardZznU6nlSQJzfP8oWQ4fKnf7/cHg8Erwzimmda7b4f89/tyziGOYywsLODO7dvoDwYSwMZErfZIvV6PwjD0hRAXhRBnlVI9xhh5bXa2OBQE/u6cdruNxcVFbDebGI1GLwRBcL42MfHD+Pj4x6WxsZ+VUqQxP78vme7TADDGEEURGo0GpqamcKRW+y4MwyellDPC837xhCgxxshBufygRQCQUqJWq2FyclJVsswRQmKpFPGVcsLzkuMnThz41R8KUkoxNjaGMAyzN06eLAgh4JyDc76vf/fGX11cvMYbUfBeAAAAAElFTkSuQmCC

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

1232
public/index.html Normal file

File diff suppressed because it is too large Load Diff

26
src/App.js Normal file
View File

@@ -0,0 +1,26 @@
import React from 'react';
import Header from './components/Header';
import Hero from './components/Hero';
import Navigation from './components/Navigation';
import MainContent from './components/MainContent';
import Sidebar from './components/Sidebar';
import Footer from './components/Footer';
function App() {
return (
<div className="min-h-screen bg-gray-50">
<Header />
<Hero />
<Navigation />
<div className="container mx-auto px-4 py-8">
<div className="flex flex-col lg:flex-row gap-8">
<MainContent />
<Sidebar />
</div>
</div>
<Footer />
</div>
);
}
export default App;

39
src/components/Footer.js Normal file
View File

@@ -0,0 +1,39 @@
import React from 'react';
import { Facebook, Instagram } from 'lucide-react';
function Footer() {
return (
<footer className="bg-gray-800 text-white py-8">
<div className="container mx-auto px-4">
<div className="text-center">
<p className="text-sm mb-4">©2025 ТОВ "Запоріжжяелектропостачання"</p>
<div className="flex justify-center items-center gap-4 mb-4">
<div className="flex items-center gap-2">
<span className="text-sm">ЗАВАНТАЖИТИ З</span>
<div className="flex gap-2">
<div className="bg-black rounded px-3 py-1">
<span className="text-xs text-white">App Store</span>
</div>
<div className="bg-black rounded px-3 py-1">
<span className="text-xs text-white">Google Play</span>
</div>
</div>
</div>
</div>
<div className="flex justify-center gap-4">
<a href="#" className="text-gray-400 hover:text-white transition-colors">
<Facebook className="w-6 h-6" />
</a>
<a href="#" className="text-gray-400 hover:text-white transition-colors">
<Instagram className="w-6 h-6" />
</a>
</div>
</div>
</div>
</footer>
);
}
export default Footer;

51
src/components/Header.js Normal file
View File

@@ -0,0 +1,51 @@
import React from 'react';
import { Search } from 'lucide-react';
function Header() {
return (
<header className="bg-white shadow-sm">
<div className="container mx-auto px-4 py-4">
<div className="flex items-center justify-between">
<div className="flex items-center gap-4">
<div className="flex items-center gap-2">
<div className="w-12 h-12 bg-gradient-to-r from-orange-400 to-yellow-400 rounded flex items-center justify-center">
<span className="text-white font-bold text-xl">З</span>
</div>
<div>
<h1 className="text-xl font-bold text-orange-500">ЗАПОРІЖЖЯЕЛЕКТРОПОСТАЧАННЯ</h1>
<p className="text-sm text-primary-600">Ваш партнер з постачання електричної енергії</p>
</div>
</div>
</div>
<div className="flex items-center gap-8">
<div className="text-right">
<p className="text-sm font-medium text-primary-600">Кол-центр:</p>
<div className="grid grid-cols-2 gap-4 text-sm">
<div>
<p className="text-primary-600">📞 061 228 22 20</p>
<p className="text-primary-600">📞 063 228 22 20</p>
</div>
<div>
<p className="text-primary-600">📞 066 228 22 20</p>
<p className="text-primary-600">📞 068 228 22 20</p>
</div>
</div>
</div>
<div className="relative">
<Search className="w-5 h-5 text-gray-400 absolute left-3 top-1/2 transform -translate-y-1/2" />
<input
type="text"
placeholder="Пошук..."
className="pl-10 pr-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-primary-500"
/>
</div>
</div>
</div>
</div>
</header>
);
}
export default Header;

38
src/components/Hero.js Normal file
View File

@@ -0,0 +1,38 @@
import React from 'react';
import { motion, useReducedMotion } from 'framer-motion';
const fadeUpPreset = (delay = 0, duration = 1.2) => ({
initial: { opacity: 0, y: 20 },
whileInView: { opacity: 1, y: 0 },
viewport: { once: true, amount: 0.2 },
transition: { delay, duration, ease: "easeOut" }
});
function Hero() {
const shouldReduce = useReducedMotion();
if (shouldReduce) {
return (
<section className="hero-bg h-64 flex items-center justify-center">
<div className="text-center text-white">
<h2 className="text-4xl font-bold mb-4">Електроенергія для вашого дому</h2>
<p className="text-xl">Надійне постачання електричної енергії</p>
</div>
</section>
);
}
return (
<motion.section
{...fadeUpPreset(0.1, 0.8)}
className="hero-bg h-64 flex items-center justify-center"
>
<div className="text-center text-white">
<h2 className="text-4xl font-bold mb-4">Електроенергія для вашого дому</h2>
<p className="text-xl">Надійне постачання електричної енергії</p>
</div>
</motion.section>
);
}
export default Hero;

View File

@@ -0,0 +1,114 @@
import React from 'react';
import { motion, useReducedMotion } from 'framer-motion';
import NewsCard from './NewsCard';
const fadeUpPreset = (delay = 0, duration = 1.2) => ({
initial: { opacity: 0, y: 20 },
whileInView: { opacity: 1, y: 0 },
viewport: { once: true, amount: 0.2 },
transition: { delay, duration, ease: "easeOut" }
});
function MainContent() {
const shouldReduce = useReducedMotion();
const newsItems = [
{
title: "Вітаємо з Різдвом Христовим!",
date: "25.12.2023",
category: "Новини",
image: "https://zpep.com.ua/images/news/christmas.jpg",
excerpt: "Дорогі українці! Від щирого серця вітаємо вас із світлим святом Різдва Христового! Нехай у кожному домі панують любов, мир і злагода, а в серці стовпиться надія та радість. Бажаємо міцного здоров'я та радості, щоб кожен день приносив щастя та натхнення! Щирих вітань!"
},
{
title: "Вітаємо із професійним святом Днем енергетика!",
date: "22.12.2023",
category: "Новини",
image: "https://zpep.com.ua/images/news/energy-day.jpg",
excerpt: "Шановні енергетики! Вітаємо вас із професійним святом! Дякуємо енергетикам України за їх професіоналізм, відданість справі та невтомну працю. Бажаємо Вам здоров'я та безпеки!"
},
{
title: "ЧАС СПЛАТИТИ РАХУНКИ ЗА ЕЛЕКТРОЕНЕРГІЮ",
date: "08.12.2023",
category: "Послуги",
image: "https://zpep.com.ua/images/news/payment.jpg",
excerpt: "Шановні споживачі електричної енергії! ТОВ «Запоріжжяелектропостачання» інформує, що розрахунок за спожиту електроенергію здійснюється щомісячно до 20 числа місяця, наступного за місяцем споживання."
},
{
title: "Тариф на послуги постачальника універсальних послуг ТОВ «ЗАПОРІЖЖЯЕЛЕКТРОПОСТАЧАННЯ»",
date: "08.12.2023",
category: "Тариф",
image: "https://zpep.com.ua/images/news/tariff.jpg",
excerpt: "Шановні споживачі! ТОВ «Запоріжжяелектропостачання» повідомляє актуальні тарифи на постачання електричної енергії."
},
{
title: "З Днем Збройних Сил України!",
date: "06.12.2023",
category: "Новини",
image: "https://zpep.com.ua/images/news/armed-forces.jpg",
excerpt: "6 грудня Україна святкує День Збройних Сил України! Шановні захисники та захисниці України! Ви є щитом нашої держави."
}
];
if (shouldReduce) {
return (
<main className="flex-1">
<div className="space-y-8">
{newsItems.map((item, index) => (
<NewsCard key={index} {...item} />
))}
</div>
<div className="flex justify-center mt-8">
<div className="flex items-center space-x-2">
<span className="text-sm text-gray-600">Сторінка 1 з 10</span>
<div className="flex space-x-1">
<button className="pagination-btn pagination-active">1</button>
<button className="pagination-btn">2</button>
<button className="pagination-btn">3</button>
<button className="pagination-btn">4</button>
<button className="pagination-btn">5</button>
<span className="px-2">...</span>
<button className="pagination-btn">Остання »</button>
</div>
</div>
</div>
</main>
);
}
return (
<motion.main
{...fadeUpPreset(0.2, 1.0)}
className="flex-1"
>
<div className="space-y-8">
{newsItems.map((item, index) => (
<motion.div
key={index}
{...fadeUpPreset(0.1 + index * 0.1, 0.8)}
>
<NewsCard {...item} />
</motion.div>
))}
</div>
<div className="flex justify-center mt-8">
<div className="flex items-center space-x-2">
<span className="text-sm text-gray-600">Сторінка 1 з 10</span>
<div className="flex space-x-1">
<button className="pagination-btn pagination-active">1</button>
<button className="pagination-btn">2</button>
<button className="pagination-btn">3</button>
<button className="pagination-btn">4</button>
<button className="pagination-btn">5</button>
<span className="px-2">...</span>
<button className="pagination-btn">Остання »</button>
</div>
</div>
</div>
</motion.main>
);
}
export default MainContent;

View File

@@ -0,0 +1,29 @@
import React from 'react';
function Navigation() {
const navItems = [
'ЕЛЕКТРОЕНЕРГІЯ',
'ПРИРОДНИЙ ГАЗ',
'НОРМАТИВНО-ПРАВОВІ АКТИ',
'ДОКУМЕНТИ ТА ІНФОРМАЦІЯ',
'ПОШИРЕНІ ЗАПИТАННЯ',
'КОНТАКТИ'
];
return (
<nav className="bg-white border-t border-b border-gray-200">
<div className="container mx-auto px-4">
<div className="flex flex-wrap items-center py-3">
<div className="nav-link text-sm font-medium mr-8">МАПА САЙТУ</div>
{navItems.map((item, index) => (
<div key={index} className="nav-link text-sm mr-6 py-2">
{item}
</div>
))}
</div>
</div>
</nav>
);
}
export default Navigation;

View File

@@ -0,0 +1,43 @@
import React from 'react';
import { Calendar, User } from 'lucide-react';
function NewsCard({ title, date, category, image, excerpt }) {
return (
<article className="news-card">
<div className="p-6">
<div className="flex items-start gap-4">
<div className="flex-shrink-0">
<img
src={image}
alt={title}
className="w-32 h-24 object-cover rounded"
/>
</div>
<div className="flex-1">
<h3 className="text-xl font-bold text-gray-900 mb-2 hover:text-primary-600 cursor-pointer">
{title}
</h3>
<div className="flex items-center gap-4 text-sm text-gray-500 mb-3">
<div className="flex items-center gap-1">
<Calendar className="w-4 h-4" />
<span>{date}</span>
</div>
<div className="flex items-center gap-1">
<User className="w-4 h-4" />
<span>{category}</span>
</div>
</div>
<p className="text-gray-700 leading-relaxed mb-4">
{excerpt}
</p>
<button className="btn-primary text-sm">
ЧИТАТИ ДАЛІ
</button>
</div>
</div>
</div>
</article>
);
}
export default NewsCard;

53
src/components/Sidebar.js Normal file
View File

@@ -0,0 +1,53 @@
import React from 'react';
import { User, Package, TrendingUp, Zap, FileText, Phone, MessageCircle } from 'lucide-react';
function Sidebar() {
const sidebarItems = [
{ icon: User, text: "ОСОБИСТИЙ КАБІНЕТ", color: "bg-orange-500" },
{ icon: Package, text: "ПЕРЕДАТИ ПОКАЗИ", color: "bg-yellow-500" },
{ icon: TrendingUp, text: "ТАРИФИ АКТУАЛЬНІ", color: "bg-yellow-600" },
{ icon: Zap, text: "СПЛАТИТИ ОНЛАЙН", color: "bg-orange-600" },
{ icon: FileText, text: "ЕЛЕКТРОННІ НА РАХУНОК", color: "bg-gray-600" },
{ icon: Phone, text: "РОЗРАХУВАТИ РОЗСТРОЧКУ", color: "bg-yellow-500" }
];
return (
<aside className="w-full lg:w-80">
<div className="space-y-2">
{sidebarItems.map((item, index) => {
const Icon = item.icon;
return (
<button key={index} className={`sidebar-btn w-full justify-start ${item.color}`}>
<Icon className="w-5 h-5" />
<span>{item.text}</span>
</button>
);
})}
</div>
<div className="mt-8 bg-white rounded-lg p-6 shadow-md">
<div className="text-center">
<div className="w-20 h-20 bg-yellow-400 rounded-full flex items-center justify-center mx-auto mb-4">
<span className="text-2xl font-bold text-white">ISO</span>
</div>
<p className="text-sm text-gray-600">Сертифікат якості</p>
<p className="text-lg font-bold text-gray-900">ISO 9001</p>
</div>
</div>
<div className="mt-6 bg-white rounded-lg p-6 shadow-md">
<h3 className="font-bold text-gray-900 mb-4">Корисні посилання</h3>
<div className="space-y-2">
<a href="#" className="block text-primary-600 hover:text-primary-800 text-sm">
Відділення нових Особових рахунків
</a>
<a href="#" className="block text-primary-600 hover:text-primary-800 text-sm">
Інструкція до особистого кабінету
</a>
</div>
</div>
</aside>
);
}
export default Sidebar;

46
src/index.css Normal file
View File

@@ -0,0 +1,46 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer base {
body {
@apply font-sans;
}
}
@layer components {
.hero-bg {
background: linear-gradient(rgba(0, 0, 0, 0.3), rgba(0, 0, 0, 0.3)), url('https://zpep.com.ua/images/bg.jpg');
background-size: cover;
background-position: center;
background-attachment: fixed;
}
.nav-link {
@apply text-gray-800 hover:text-primary-600 transition-colors duration-200 font-medium;
}
.btn-primary {
@apply bg-primary-600 text-white px-6 py-2 rounded hover:bg-primary-700 transition-colors duration-200;
}
.btn-orange {
@apply bg-orange-500 text-white px-4 py-2 rounded hover:bg-orange-600 transition-colors duration-200;
}
.sidebar-btn {
@apply bg-orange-500 text-white px-3 py-2 rounded-r text-sm font-medium hover:bg-orange-600 transition-colors duration-200 flex items-center gap-2;
}
.news-card {
@apply bg-white rounded-lg shadow-md overflow-hidden hover:shadow-lg transition-shadow duration-200;
}
.pagination-btn {
@apply px-3 py-1 mx-1 rounded hover:bg-primary-100 transition-colors duration-200;
}
.pagination-active {
@apply bg-primary-600 text-white;
}
}

13
src/index.js Normal file
View File

@@ -0,0 +1,13 @@
import React from 'react';
import { createRoot } from 'react-dom/client';
import './index.css';
import App from './App';
const container = document.getElementById('root');
const root = createRoot(container);
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);

41
tailwind.config.js Normal file
View File

@@ -0,0 +1,41 @@
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
"./src/**/*.{js,jsx,ts,tsx}",
"./public/index.html"
],
theme: {
extend: {
colors: {
primary: {
50: '#f0f9ff',
100: '#e0f2fe',
200: '#bae6fd',
300: '#7dd3fc',
400: '#38bdf8',
500: '#0ea5e9',
600: '#0284c7',
700: '#0369a1',
800: '#075985',
900: '#0c4a6e'
},
orange: {
400: '#fb923c',
500: '#f97316',
600: '#ea580c'
},
yellow: {
400: '#facc15',
500: '#eab308'
}
},
fontFamily: {
sans: ['Arial', 'sans-serif']
},
backgroundImage: {
'hero-pattern': 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)'
}
}
},
plugins: []
}

5
vercel.json Normal file
View File

@@ -0,0 +1,5 @@
{
"installCommand": "npm install",
"buildCommand": "CI=false npm run build",
"outputDirectory": "build"
}