Compare commits
43 Commits
v2.0.1
...
feature/pr
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7f20353674 | ||
|
|
bef69da0d1 | ||
|
|
f80ec9797e | ||
|
|
fbbd820512 | ||
|
|
5d0777f67c | ||
|
|
f1d504f985 | ||
|
|
6a4d77d904 | ||
|
|
c35d53266a | ||
|
|
dc096f5e12 | ||
|
|
a681aadcfa | ||
|
|
f15f3f5110 | ||
|
|
07ae71fd23 | ||
|
|
26270b2842 | ||
|
|
0d0596b767 | ||
|
|
eca7af2d6f | ||
|
|
3ace14fcdb | ||
|
|
99bec6c7f9 | ||
|
|
ef299f1f4a | ||
|
|
66b0709e6e | ||
|
|
a2ed33214d | ||
|
|
a3dccd690d | ||
|
|
69313fba34 | ||
|
|
1889c58598 | ||
|
|
e9f0162439 | ||
|
|
323b17185c | ||
|
|
09f44a5685 | ||
|
|
f4ab7841fb | ||
|
|
3de4f69a62 | ||
|
|
102be2c24a | ||
|
|
3a39b25e64 | ||
|
|
32248f8424 | ||
|
|
a1f8776743 | ||
|
|
7e431d645a | ||
|
|
cef4bffd69 | ||
|
|
51767ebbdb | ||
|
|
ad91148616 | ||
|
|
38df66044d | ||
|
|
75391641fd | ||
|
|
24dc99a19a | ||
|
|
907075f51d | ||
|
|
b48ac45085 | ||
|
|
b115d0a772 | ||
|
|
ad0cdfe1d9 |
35
README.md
35
README.md
@@ -1,6 +1,6 @@
|
|||||||
<h1 align="center">
|
<h1 align="center">
|
||||||
<a href="https://worklenz.com" target="_blank" rel="noopener noreferrer">
|
<a href="https://worklenz.com" target="_blank" rel="noopener noreferrer">
|
||||||
<img src="https://app.worklenz.com/assets/icons/icon-144x144.png" alt="Worklenz Logo" width="75">
|
<img src="https://s3.us-west-2.amazonaws.com/worklenz.com/assets/icon-144x144.png" alt="Worklenz Logo" width="75">
|
||||||
</a>
|
</a>
|
||||||
<br>
|
<br>
|
||||||
Worklenz
|
Worklenz
|
||||||
@@ -69,8 +69,7 @@ cd worklenz
|
|||||||
2. Set up environment variables
|
2. Set up environment variables
|
||||||
- Copy the example environment files
|
- Copy the example environment files
|
||||||
```bash
|
```bash
|
||||||
cp .env.example .env
|
cp worklenz-backend/.env.template worklenz-backend/.env
|
||||||
cp worklenz-backend/.env.example worklenz-backend/.env
|
|
||||||
```
|
```
|
||||||
- Update the environment variables with your configuration
|
- Update the environment variables with your configuration
|
||||||
|
|
||||||
@@ -192,6 +191,27 @@ Email [info@worklenz.com](mailto:info@worklenz.com) to disclose any security vul
|
|||||||
|
|
||||||
This project is licensed under the [MIT License](LICENSE).
|
This project is licensed under the [MIT License](LICENSE).
|
||||||
|
|
||||||
|
## Analytics
|
||||||
|
|
||||||
|
Worklenz uses Google Analytics to understand how the application is being used. This helps us improve the application and make better decisions about future development.
|
||||||
|
|
||||||
|
### What We Track
|
||||||
|
- Anonymous usage statistics
|
||||||
|
- Page views and navigation patterns
|
||||||
|
- Feature usage
|
||||||
|
- Browser and device information
|
||||||
|
|
||||||
|
### Privacy
|
||||||
|
- Analytics is opt-in only
|
||||||
|
- No personal information is collected
|
||||||
|
- Users can opt-out at any time
|
||||||
|
- Data is stored according to Google's privacy policy
|
||||||
|
|
||||||
|
### How to Opt-Out
|
||||||
|
If you've previously opted in and want to opt-out:
|
||||||
|
1. Clear your browser's local storage for the Worklenz domain
|
||||||
|
2. Or click the "Decline" button in the analytics notice if it appears
|
||||||
|
|
||||||
## Screenshots
|
## Screenshots
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
@@ -315,6 +335,7 @@ docker-compose up -d
|
|||||||
docker-compose down
|
docker-compose down
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
## MinIO Integration
|
## MinIO Integration
|
||||||
|
|
||||||
The project uses MinIO as an S3-compatible object storage service, which provides an open-source alternative to AWS S3 for development and production.
|
The project uses MinIO as an S3-compatible object storage service, which provides an open-source alternative to AWS S3 for development and production.
|
||||||
@@ -403,6 +424,10 @@ This script generates properly configured environment files for both development
|
|||||||
- Frontend: http://localhost:5000
|
- Frontend: http://localhost:5000
|
||||||
- Backend API: http://localhost:3000 (or https://localhost:3000 with SSL)
|
- Backend API: http://localhost:3000 (or https://localhost:3000 with SSL)
|
||||||
|
|
||||||
|
4. Video Guide
|
||||||
|
|
||||||
|
For a visual walkthrough of the local Docker deployment process, check out our [step-by-step video guide](https://www.youtube.com/watch?v=AfwAKxJbqLg).
|
||||||
|
|
||||||
### Remote Server Deployment
|
### Remote Server Deployment
|
||||||
|
|
||||||
When deploying to a remote server:
|
When deploying to a remote server:
|
||||||
@@ -428,6 +453,10 @@ When deploying to a remote server:
|
|||||||
- Frontend: http://your-server-hostname:5000
|
- Frontend: http://your-server-hostname:5000
|
||||||
- Backend API: http://your-server-hostname:3000
|
- Backend API: http://your-server-hostname:3000
|
||||||
|
|
||||||
|
4. Video Guide
|
||||||
|
|
||||||
|
For a complete walkthrough of deploying Worklenz to a remote server, check out our [deployment video guide](https://www.youtube.com/watch?v=CAZGu2iOXQs&t=10s).
|
||||||
|
|
||||||
### Environment Configuration
|
### Environment Configuration
|
||||||
|
|
||||||
The Docker setup uses environment variables to configure the services:
|
The Docker setup uses environment variables to configure the services:
|
||||||
|
|||||||
@@ -7,15 +7,17 @@ services:
|
|||||||
ports:
|
ports:
|
||||||
- "5000:5000"
|
- "5000:5000"
|
||||||
depends_on:
|
depends_on:
|
||||||
backend:
|
- backend
|
||||||
condition: service_started
|
restart: unless-stopped
|
||||||
env_file:
|
env_file:
|
||||||
- ./worklenz-frontend/.env.production
|
- ./worklenz-frontend/.env.production
|
||||||
networks:
|
networks:
|
||||||
- worklenz
|
- worklenz
|
||||||
|
|
||||||
backend:
|
backend:
|
||||||
image: docker.io/chamikajaycey/worklenz-backend:latest
|
build:
|
||||||
|
context: ./worklenz-backend
|
||||||
|
dockerfile: Dockerfile
|
||||||
container_name: worklenz_backend
|
container_name: worklenz_backend
|
||||||
ports:
|
ports:
|
||||||
- "3000:3000"
|
- "3000:3000"
|
||||||
@@ -24,6 +26,7 @@ services:
|
|||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
minio:
|
minio:
|
||||||
condition: service_started
|
condition: service_started
|
||||||
|
restart: unless-stopped
|
||||||
env_file:
|
env_file:
|
||||||
- ./worklenz-backend/.env
|
- ./worklenz-backend/.env
|
||||||
networks:
|
networks:
|
||||||
@@ -35,6 +38,7 @@ services:
|
|||||||
ports:
|
ports:
|
||||||
- "9000:9000"
|
- "9000:9000"
|
||||||
- "9001:9001"
|
- "9001:9001"
|
||||||
|
restart: unless-stopped
|
||||||
environment:
|
environment:
|
||||||
MINIO_ROOT_USER: ${S3_ACCESS_KEY_ID:-minioadmin}
|
MINIO_ROOT_USER: ${S3_ACCESS_KEY_ID:-minioadmin}
|
||||||
MINIO_ROOT_PASSWORD: ${S3_SECRET_ACCESS_KEY:-minioadmin}
|
MINIO_ROOT_PASSWORD: ${S3_SECRET_ACCESS_KEY:-minioadmin}
|
||||||
@@ -50,29 +54,27 @@ services:
|
|||||||
container_name: worklenz_createbuckets
|
container_name: worklenz_createbuckets
|
||||||
depends_on:
|
depends_on:
|
||||||
- minio
|
- minio
|
||||||
|
restart: on-failure
|
||||||
entrypoint: >
|
entrypoint: >
|
||||||
/bin/sh -c "
|
/bin/sh -c '
|
||||||
# Wait for MinIO to be available
|
echo "Waiting for MinIO to start...";
|
||||||
echo 'Waiting for MinIO to start...'
|
sleep 15;
|
||||||
sleep 15;
|
for i in 1 2 3 4 5; do
|
||||||
# Retry up to 5 times
|
echo "Attempt $i to connect to MinIO...";
|
||||||
for i in 1 2 3 4 5; do
|
if /usr/bin/mc alias set myminio http://minio:9000 minioadmin minioadmin; then
|
||||||
echo \"Attempt $$i to connect to MinIO...\"
|
echo "Successfully connected to MinIO!";
|
||||||
if /usr/bin/mc config host add myminio http://minio:9000 minioadmin minioadmin; then
|
/usr/bin/mc mb --ignore-existing myminio/worklenz-bucket;
|
||||||
echo \"Successfully connected to MinIO!\"
|
/usr/bin/mc policy set public myminio/worklenz-bucket;
|
||||||
/usr/bin/mc mb --ignore-existing myminio/worklenz-bucket;
|
exit 0;
|
||||||
/usr/bin/mc policy set public myminio/worklenz-bucket;
|
fi
|
||||||
exit 0;
|
echo "Connection failed, retrying in 5 seconds...";
|
||||||
fi
|
sleep 5;
|
||||||
echo \"Connection failed, retrying in 5 seconds...\"
|
done;
|
||||||
sleep 5;
|
echo "Failed to connect to MinIO after 5 attempts";
|
||||||
done
|
exit 1;
|
||||||
echo \"Failed to connect to MinIO after 5 attempts\"
|
'
|
||||||
exit 1;
|
|
||||||
"
|
|
||||||
networks:
|
networks:
|
||||||
- worklenz
|
- worklenz
|
||||||
|
|
||||||
db:
|
db:
|
||||||
image: postgres:15
|
image: postgres:15
|
||||||
container_name: worklenz_db
|
container_name: worklenz_db
|
||||||
@@ -85,6 +87,7 @@ services:
|
|||||||
interval: 10s
|
interval: 10s
|
||||||
timeout: 5s
|
timeout: 5s
|
||||||
retries: 5
|
retries: 5
|
||||||
|
restart: unless-stopped
|
||||||
networks:
|
networks:
|
||||||
- worklenz
|
- worklenz
|
||||||
volumes:
|
volumes:
|
||||||
@@ -94,22 +97,19 @@ services:
|
|||||||
target: /docker-entrypoint-initdb.d
|
target: /docker-entrypoint-initdb.d
|
||||||
consistency: cached
|
consistency: cached
|
||||||
command: >
|
command: >
|
||||||
bash -c '
|
bash -c ' if command -v apt-get >/dev/null 2>&1; then
|
||||||
if command -v apt-get >/dev/null 2>&1; then
|
|
||||||
apt-get update && apt-get install -y dos2unix
|
apt-get update && apt-get install -y dos2unix
|
||||||
elif command -v apk >/dev/null 2>&1; then
|
elif command -v apk >/dev/null 2>&1; then
|
||||||
apk add --no-cache dos2unix
|
apk add --no-cache dos2unix
|
||||||
fi &&
|
fi && find /docker-entrypoint-initdb.d -type f -name "*.sh" -exec sh -c '\''
|
||||||
find /docker-entrypoint-initdb.d -type f -name "*.sh" -exec sh -c '\''
|
|
||||||
dos2unix "{}" 2>/dev/null || true
|
dos2unix "{}" 2>/dev/null || true
|
||||||
chmod +x "{}"
|
chmod +x "{}"
|
||||||
'\'' \; &&
|
'\'' \; && exec docker-entrypoint.sh postgres '
|
||||||
exec docker-entrypoint.sh postgres
|
|
||||||
'
|
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
worklenz_postgres_data:
|
worklenz_postgres_data:
|
||||||
worklenz_minio_data:
|
worklenz_minio_data:
|
||||||
|
|
||||||
|
|
||||||
networks:
|
networks:
|
||||||
worklenz:
|
worklenz:
|
||||||
|
|||||||
24
start.bat
24
start.bat
@@ -39,6 +39,12 @@ IF %ERRORLEVEL% NEQ 0 (
|
|||||||
echo docker-compose is installed >> worklenz_startup.log
|
echo docker-compose is installed >> worklenz_startup.log
|
||||||
)
|
)
|
||||||
|
|
||||||
|
REM Check for update-docker-env.sh
|
||||||
|
IF EXIST update-docker-env.sh (
|
||||||
|
echo [94mFound update-docker-env.sh script. You can use it to update environment variables.[0m
|
||||||
|
echo Found update-docker-env.sh script >> worklenz_startup.log
|
||||||
|
)
|
||||||
|
|
||||||
REM Run preflight checks
|
REM Run preflight checks
|
||||||
echo Running Docker daemon check...
|
echo Running Docker daemon check...
|
||||||
docker info >nul 2>>worklenz_startup.log
|
docker info >nul 2>>worklenz_startup.log
|
||||||
@@ -52,17 +58,6 @@ IF %ERRORLEVEL% NEQ 0 (
|
|||||||
echo Docker daemon is running >> worklenz_startup.log
|
echo Docker daemon is running >> worklenz_startup.log
|
||||||
)
|
)
|
||||||
|
|
||||||
REM Check if .env file exists
|
|
||||||
IF NOT EXIST .env (
|
|
||||||
echo Warning: .env file not found. Using default configuration.
|
|
||||||
echo Warning: .env file not found. Using default configuration. >> worklenz_startup.log
|
|
||||||
IF EXIST .env.example (
|
|
||||||
copy .env.example .env
|
|
||||||
echo Created .env file from .env.example
|
|
||||||
echo Created .env file from .env.example >> worklenz_startup.log
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
REM Stop any running containers
|
REM Stop any running containers
|
||||||
echo Stopping any running containers...
|
echo Stopping any running containers...
|
||||||
docker-compose down > nul 2>>worklenz_startup.log
|
docker-compose down > nul 2>>worklenz_startup.log
|
||||||
@@ -111,7 +106,7 @@ REM Check frontend
|
|||||||
findstr /C:"frontend" running_services.txt > nul
|
findstr /C:"frontend" running_services.txt > nul
|
||||||
IF %ERRORLEVEL% EQU 0 (
|
IF %ERRORLEVEL% EQU 0 (
|
||||||
echo [92m^✓[0m Frontend is running
|
echo [92m^✓[0m Frontend is running
|
||||||
echo Frontend URL: http://localhost:5000
|
echo Frontend URL: http://localhost:5000 (or https://localhost:5000 if SSL is enabled)
|
||||||
echo Frontend is running >> worklenz_startup.log
|
echo Frontend is running >> worklenz_startup.log
|
||||||
) ELSE (
|
) ELSE (
|
||||||
echo [91m^✗[0m Frontend service failed to start
|
echo [91m^✗[0m Frontend service failed to start
|
||||||
@@ -122,7 +117,7 @@ REM Check backend
|
|||||||
findstr /C:"backend" running_services.txt > nul
|
findstr /C:"backend" running_services.txt > nul
|
||||||
IF %ERRORLEVEL% EQU 0 (
|
IF %ERRORLEVEL% EQU 0 (
|
||||||
echo [92m^✓[0m Backend is running
|
echo [92m^✓[0m Backend is running
|
||||||
echo Backend URL: http://localhost:3000
|
echo Backend URL: http://localhost:3000 (or https://localhost:3000 if SSL is enabled)
|
||||||
echo Backend is running >> worklenz_startup.log
|
echo Backend is running >> worklenz_startup.log
|
||||||
) ELSE (
|
) ELSE (
|
||||||
echo [91m^✗[0m Backend service failed to start
|
echo [91m^✗[0m Backend service failed to start
|
||||||
@@ -180,6 +175,9 @@ IF %allRunning% EQU 1 (
|
|||||||
|
|
||||||
echo You can access the application at: http://localhost:5000
|
echo You can access the application at: http://localhost:5000
|
||||||
echo To stop the services, run: stop.bat
|
echo To stop the services, run: stop.bat
|
||||||
|
echo To update environment variables, run: update-docker-env.sh
|
||||||
|
echo.
|
||||||
|
echo Note: To enable SSL, set ENABLE_SSL=true in your .env file and run update-docker-env.sh
|
||||||
echo.
|
echo.
|
||||||
echo For any errors, check worklenz_startup.log file
|
echo For any errors, check worklenz_startup.log file
|
||||||
echo.
|
echo.
|
||||||
|
|||||||
21
start.sh
21
start.sh
@@ -20,14 +20,9 @@ echo " W O R K L E N Z "
|
|||||||
echo -e "${NC}"
|
echo -e "${NC}"
|
||||||
echo "Starting Worklenz Docker Environment..."
|
echo "Starting Worklenz Docker Environment..."
|
||||||
|
|
||||||
# Check if .env file exists
|
# Check if update-docker-env.sh exists and is executable
|
||||||
if [ ! -f .env ]; then
|
if [ -f update-docker-env.sh ] && [ -x update-docker-env.sh ]; then
|
||||||
echo -e "${YELLOW}Warning: .env file not found. Using default configuration.${NC}"
|
echo -e "${BLUE}Found update-docker-env.sh script. You can use it to update environment variables.${NC}"
|
||||||
# Copy the example .env file if it exists
|
|
||||||
if [ -f .env.example ]; then
|
|
||||||
cp .env.example .env
|
|
||||||
echo "Created .env file from .env.example"
|
|
||||||
fi
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Function to check if a service is running
|
# Function to check if a service is running
|
||||||
@@ -129,7 +124,7 @@ DB_STATUS=$?
|
|||||||
check_service "MinIO" "worklenz_minio" "http://localhost:9000/minio/health/live"
|
check_service "MinIO" "worklenz_minio" "http://localhost:9000/minio/health/live"
|
||||||
MINIO_STATUS=$?
|
MINIO_STATUS=$?
|
||||||
|
|
||||||
check_service "Backend" "worklenz_backend" "http://localhost:3000/api/health"
|
check_service "Backend" "worklenz_backend" "http://localhost:3000/public/health"
|
||||||
BACKEND_STATUS=$?
|
BACKEND_STATUS=$?
|
||||||
|
|
||||||
check_service "Frontend" "worklenz_frontend" "http://localhost:5000"
|
check_service "Frontend" "worklenz_frontend" "http://localhost:5000"
|
||||||
@@ -137,8 +132,8 @@ FRONTEND_STATUS=$?
|
|||||||
|
|
||||||
# Display service URLs
|
# Display service URLs
|
||||||
echo -e "\n${BLUE}Service URLs:${NC}"
|
echo -e "\n${BLUE}Service URLs:${NC}"
|
||||||
[ $FRONTEND_STATUS -eq 0 ] && echo " • Frontend: http://localhost:5000"
|
[ $FRONTEND_STATUS -eq 0 ] && echo " • Frontend: http://localhost:5000 (or https://localhost:5000 if SSL is enabled)"
|
||||||
[ $BACKEND_STATUS -eq 0 ] && echo " • Backend API: http://localhost:3000"
|
[ $BACKEND_STATUS -eq 0 ] && echo " • Backend API: http://localhost:3000 (or https://localhost:3000 if SSL is enabled)"
|
||||||
[ $MINIO_STATUS -eq 0 ] && echo " • MinIO Console: http://localhost:9001 (login: minioadmin/minioadmin)"
|
[ $MINIO_STATUS -eq 0 ] && echo " • MinIO Console: http://localhost:9001 (login: minioadmin/minioadmin)"
|
||||||
|
|
||||||
# Check if all services are up
|
# Check if all services are up
|
||||||
@@ -151,4 +146,6 @@ fi
|
|||||||
|
|
||||||
echo -e "\n${BLUE}Useful commands:${NC}"
|
echo -e "\n${BLUE}Useful commands:${NC}"
|
||||||
echo " • View logs: $DOCKER_COMPOSE_CMD logs -f"
|
echo " • View logs: $DOCKER_COMPOSE_CMD logs -f"
|
||||||
echo " • Stop services: ./stop.sh"
|
echo " • Stop services: ./stop.sh"
|
||||||
|
echo " • Update environment variables: ./update-docker-env.sh"
|
||||||
|
echo -e "\n${YELLOW}Note:${NC} To enable SSL, set ENABLE_SSL=true in your .env file and run ./update-docker-env.sh"
|
||||||
@@ -73,13 +73,21 @@ cat > worklenz-backend/.env << EOL
|
|||||||
NODE_ENV=production
|
NODE_ENV=production
|
||||||
PORT=3000
|
PORT=3000
|
||||||
SESSION_NAME=worklenz.sid
|
SESSION_NAME=worklenz.sid
|
||||||
SESSION_SECRET=change_me_in_production
|
SESSION_SECRET=$(openssl rand -base64 48)
|
||||||
COOKIE_SECRET=change_me_in_production
|
COOKIE_SECRET=$(openssl rand -base64 48)
|
||||||
|
|
||||||
# CORS
|
# CORS
|
||||||
SOCKET_IO_CORS=${FRONTEND_URL}
|
SOCKET_IO_CORS=${FRONTEND_URL}
|
||||||
SERVER_CORS=${FRONTEND_URL}
|
SERVER_CORS=${FRONTEND_URL}
|
||||||
|
|
||||||
|
|
||||||
|
# Google Login
|
||||||
|
GOOGLE_CLIENT_ID="your_google_client_id"
|
||||||
|
GOOGLE_CLIENT_SECRET="your_google_client_secret"
|
||||||
|
GOOGLE_CALLBACK_URL="${FRONTEND_URL}/secure/google/verify"
|
||||||
|
LOGIN_FAILURE_REDIRECT="${FRONTEND_URL}/auth/authenticating"
|
||||||
|
LOGIN_SUCCESS_REDIRECT="${FRONTEND_URL}/auth/authenticating"
|
||||||
|
|
||||||
# Database
|
# Database
|
||||||
DB_HOST=db
|
DB_HOST=db
|
||||||
DB_PORT=5432
|
DB_PORT=5432
|
||||||
@@ -115,7 +123,7 @@ SLACK_WEBHOOK=
|
|||||||
COMMIT_BUILD_IMMEDIATELY=true
|
COMMIT_BUILD_IMMEDIATELY=true
|
||||||
|
|
||||||
# JWT Secret
|
# JWT Secret
|
||||||
JWT_SECRET=change_me_in_production
|
JWT_SECRET=$(openssl rand -base64 48)
|
||||||
EOL
|
EOL
|
||||||
|
|
||||||
echo "Environment configuration updated for ${HOSTNAME} with" $([ "$USE_SSL" = "true" ] && echo "HTTPS/WSS" || echo "HTTP/WS")
|
echo "Environment configuration updated for ${HOSTNAME} with" $([ "$USE_SSL" = "true" ] && echo "HTTPS/WSS" || echo "HTTP/WS")
|
||||||
@@ -130,4 +138,4 @@ echo "Frontend URL: ${FRONTEND_URL}"
|
|||||||
echo "API URL: ${HTTP_PREFIX}${HOSTNAME}:3000"
|
echo "API URL: ${HTTP_PREFIX}${HOSTNAME}:3000"
|
||||||
echo "Socket URL: ${WS_PREFIX}${HOSTNAME}:3000"
|
echo "Socket URL: ${WS_PREFIX}${HOSTNAME}:3000"
|
||||||
echo "MinIO Dashboard URL: ${MINIO_DASHBOARD_URL}"
|
echo "MinIO Dashboard URL: ${MINIO_DASHBOARD_URL}"
|
||||||
echo "CORS is configured to allow requests from: ${FRONTEND_URL}"
|
echo "CORS is configured to allow requests from: ${FRONTEND_URL}"
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
node_modules
|
node_modules
|
||||||
npm-debug.log
|
npm-debug.log
|
||||||
build
|
|
||||||
.scannerwork
|
.scannerwork
|
||||||
coverage
|
coverage
|
||||||
|
.dockerignore
|
||||||
|
.git
|
||||||
|
*.md
|
||||||
|
tests
|
||||||
|
|
||||||
|
|||||||
@@ -1,26 +1,39 @@
|
|||||||
# Use the official Node.js 20 image as a base
|
# --- Stage 1: Build ---
|
||||||
FROM node:20
|
FROM node:20-slim AS builder
|
||||||
|
|
||||||
|
ARG RELEASE_VERSION
|
||||||
|
RUN apt-get update && apt-get install -y \
|
||||||
|
python3 \
|
||||||
|
make \
|
||||||
|
g++ \
|
||||||
|
curl \
|
||||||
|
postgresql-server-dev-all \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
# Create and set the working directory
|
|
||||||
WORKDIR /usr/src/app
|
WORKDIR /usr/src/app
|
||||||
|
|
||||||
# Install global dependencies
|
|
||||||
RUN npm install -g ts-node typescript grunt grunt-cli
|
|
||||||
|
|
||||||
# Copy package.json and package-lock.json (if available)
|
|
||||||
COPY package*.json ./
|
COPY package*.json ./
|
||||||
|
|
||||||
# Install app dependencies
|
|
||||||
RUN npm ci
|
RUN npm ci
|
||||||
|
|
||||||
# Copy the rest of the application code
|
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
# Run the build script to compile TypeScript to JavaScript
|
|
||||||
RUN npm run build
|
RUN npm run build
|
||||||
|
|
||||||
# Expose the port the app runs on
|
RUN echo "$RELEASE_VERSION" > release
|
||||||
EXPOSE 3000
|
|
||||||
|
# --- Stage 2: Production Image ---
|
||||||
|
FROM node:20-slim
|
||||||
|
|
||||||
|
RUN apt-get update && apt-get install -y libpq5 && rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY --from=builder /usr/src/app/package*.json ./
|
||||||
|
COPY --from=builder /usr/src/app/build ./build
|
||||||
|
COPY --from=builder /usr/src/app/node_modules ./node_modules
|
||||||
|
COPY --from=builder /usr/src/app/release ./release
|
||||||
|
COPY --from=builder /usr/src/app/worklenz-email-templates ./worklenz-email-templates
|
||||||
|
|
||||||
|
|
||||||
|
EXPOSE 3000
|
||||||
|
CMD ["node", "build/bin/www"]
|
||||||
|
|
||||||
# Start the application
|
|
||||||
CMD ["npm", "start"]
|
|
||||||
@@ -12,7 +12,7 @@ CREATE TYPE DEPENDENCY_TYPE AS ENUM ('blocked_by');
|
|||||||
|
|
||||||
CREATE TYPE SCHEDULE_TYPE AS ENUM ('daily', 'weekly', 'yearly', 'monthly', 'every_x_days', 'every_x_weeks', 'every_x_months');
|
CREATE TYPE SCHEDULE_TYPE AS ENUM ('daily', 'weekly', 'yearly', 'monthly', 'every_x_days', 'every_x_weeks', 'every_x_months');
|
||||||
|
|
||||||
CREATE TYPE LANGUAGE_TYPE AS ENUM ('en', 'es', 'pt');
|
CREATE TYPE LANGUAGE_TYPE AS ENUM ('en', 'es', 'pt', 'alb', 'de');
|
||||||
|
|
||||||
-- START: Users
|
-- START: Users
|
||||||
CREATE SEQUENCE IF NOT EXISTS users_user_no_seq START 1;
|
CREATE SEQUENCE IF NOT EXISTS users_user_no_seq START 1;
|
||||||
|
|||||||
@@ -1,18 +1,17 @@
|
|||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
import {IWorkLenzRequest} from "../interfaces/worklenz-request";
|
import Excel from "exceljs";
|
||||||
import {IWorkLenzResponse} from "../interfaces/worklenz-response";
|
import { IWorkLenzRequest } from "../interfaces/worklenz-request";
|
||||||
|
import { IWorkLenzResponse } from "../interfaces/worklenz-response";
|
||||||
import db from "../config/db";
|
import db from "../config/db";
|
||||||
|
import { ServerResponse } from "../models/server-response";
|
||||||
import {ServerResponse} from "../models/server-response";
|
|
||||||
import WorklenzControllerBase from "./worklenz-controller-base";
|
import WorklenzControllerBase from "./worklenz-controller-base";
|
||||||
import HandleExceptions from "../decorators/handle-exceptions";
|
import HandleExceptions from "../decorators/handle-exceptions";
|
||||||
import {formatDuration, formatLogText, getColor} from "../shared/utils";
|
import { formatDuration, formatLogText, getColor } from "../shared/utils";
|
||||||
|
|
||||||
export default class ActivitylogsController extends WorklenzControllerBase {
|
export default class ActivitylogsController extends WorklenzControllerBase {
|
||||||
@HandleExceptions()
|
@HandleExceptions()
|
||||||
public static async get(req: IWorkLenzRequest, res: IWorkLenzResponse): Promise<IWorkLenzResponse> {
|
public static async get(req: IWorkLenzRequest, res: IWorkLenzResponse): Promise<IWorkLenzResponse> {
|
||||||
const {id} = req.params;
|
const { id } = req.params;
|
||||||
const q = `SELECT get_activity_logs_by_task($1) AS activity_logs;`;
|
const q = `SELECT get_activity_logs_by_task($1) AS activity_logs;`;
|
||||||
const result = await db.query(q, [id]);
|
const result = await db.query(q, [id]);
|
||||||
const [data] = result.rows;
|
const [data] = result.rows;
|
||||||
@@ -31,4 +30,196 @@ export default class ActivitylogsController extends WorklenzControllerBase {
|
|||||||
|
|
||||||
return res.status(200).send(new ServerResponse(true, data.activity_logs));
|
return res.status(200).send(new ServerResponse(true, data.activity_logs));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
@HandleExceptions()
|
||||||
|
public static async getByProjectId(req: IWorkLenzRequest, res: IWorkLenzResponse): Promise<IWorkLenzResponse> {
|
||||||
|
try {
|
||||||
|
console.log("Received request for project activity logs:", req.params, req.query);
|
||||||
|
const projectId = req.params.id;
|
||||||
|
const page = parseInt(req.query.page as string) || 1;
|
||||||
|
const size = parseInt(req.query.size as string) || 20;
|
||||||
|
const offset = (page - 1) * size;
|
||||||
|
const filterType = req.query.filter as string || "all";
|
||||||
|
|
||||||
|
// Add filter conditions
|
||||||
|
let filterClause = "";
|
||||||
|
const filterParams = [projectId];
|
||||||
|
let paramIndex = 2;
|
||||||
|
|
||||||
|
if (filterType && filterType !== "all") {
|
||||||
|
filterClause = ` AND tal.attribute_type = $${paramIndex}`;
|
||||||
|
filterParams.push(filterType);
|
||||||
|
paramIndex++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Defensive UUID regex for safe casting
|
||||||
|
const uuidRegex = "'^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$'";
|
||||||
|
|
||||||
|
const q = `
|
||||||
|
SELECT
|
||||||
|
tal.id,
|
||||||
|
tal.task_id,
|
||||||
|
tal.attribute_type,
|
||||||
|
tal.log_type,
|
||||||
|
tal.old_value,
|
||||||
|
tal.new_value,
|
||||||
|
tal.prev_string,
|
||||||
|
tal.next_string,
|
||||||
|
tal.created_at,
|
||||||
|
|
||||||
|
-- Task details
|
||||||
|
(SELECT name FROM tasks WHERE id = tal.task_id) AS task_name,
|
||||||
|
(SELECT task_no FROM tasks WHERE id = tal.task_id) AS task_no,
|
||||||
|
CONCAT((SELECT key FROM projects WHERE id = $1), '-', (SELECT task_no FROM tasks WHERE id = tal.task_id)) AS task_key,
|
||||||
|
|
||||||
|
-- User details
|
||||||
|
(SELECT ROW_TO_JSON(user_data) FROM (
|
||||||
|
SELECT
|
||||||
|
u.id,
|
||||||
|
u.name,
|
||||||
|
u.avatar_url,
|
||||||
|
u.email
|
||||||
|
FROM users u
|
||||||
|
WHERE u.id = tal.user_id
|
||||||
|
) user_data) AS done_by,
|
||||||
|
|
||||||
|
-- Status details for status changes (safe UUID cast)
|
||||||
|
CASE
|
||||||
|
WHEN tal.attribute_type = 'status' AND tal.old_value ~ ${uuidRegex} THEN
|
||||||
|
(SELECT ROW_TO_JSON(status_data) FROM (
|
||||||
|
SELECT
|
||||||
|
ts.name
|
||||||
|
FROM task_statuses ts
|
||||||
|
WHERE ts.id = tal.old_value::UUID
|
||||||
|
) status_data)
|
||||||
|
ELSE NULL
|
||||||
|
END AS previous_status,
|
||||||
|
|
||||||
|
CASE
|
||||||
|
WHEN tal.attribute_type = 'status' AND tal.new_value ~ ${uuidRegex} THEN
|
||||||
|
(SELECT ROW_TO_JSON(status_data) FROM (
|
||||||
|
SELECT
|
||||||
|
ts.name
|
||||||
|
FROM task_statuses ts
|
||||||
|
WHERE ts.id = tal.new_value::UUID
|
||||||
|
) status_data)
|
||||||
|
ELSE NULL
|
||||||
|
END AS next_status,
|
||||||
|
|
||||||
|
-- Priority details for priority changes (safe UUID cast)
|
||||||
|
CASE
|
||||||
|
WHEN tal.attribute_type = 'priority' AND tal.old_value ~ ${uuidRegex} THEN
|
||||||
|
(SELECT ROW_TO_JSON(priority_data) FROM (
|
||||||
|
SELECT
|
||||||
|
tp.name
|
||||||
|
FROM task_priorities tp
|
||||||
|
WHERE tp.id = tal.old_value::UUID
|
||||||
|
) priority_data)
|
||||||
|
ELSE NULL
|
||||||
|
END AS previous_priority,
|
||||||
|
|
||||||
|
CASE
|
||||||
|
WHEN tal.attribute_type = 'priority' AND tal.new_value ~ ${uuidRegex} THEN
|
||||||
|
(SELECT ROW_TO_JSON(priority_data) FROM (
|
||||||
|
SELECT
|
||||||
|
tp.name
|
||||||
|
FROM task_priorities tp
|
||||||
|
WHERE tp.id = tal.new_value::UUID
|
||||||
|
) priority_data)
|
||||||
|
ELSE NULL
|
||||||
|
END AS next_priority,
|
||||||
|
|
||||||
|
-- Assigned user details for assignee changes (safe UUID cast)
|
||||||
|
CASE
|
||||||
|
WHEN tal.attribute_type = 'assignee' AND tal.new_value ~ ${uuidRegex} THEN
|
||||||
|
(SELECT ROW_TO_JSON(user_data) FROM (
|
||||||
|
SELECT
|
||||||
|
u.id,
|
||||||
|
u.name,
|
||||||
|
u.avatar_url,
|
||||||
|
u.email
|
||||||
|
FROM users u
|
||||||
|
WHERE u.id = tal.new_value::UUID
|
||||||
|
) user_data)
|
||||||
|
ELSE NULL
|
||||||
|
END AS assigned_user
|
||||||
|
|
||||||
|
FROM task_activity_logs tal
|
||||||
|
WHERE tal.project_id = $1${filterClause}
|
||||||
|
ORDER BY tal.created_at DESC
|
||||||
|
LIMIT $${paramIndex} OFFSET $${paramIndex + 1}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const countQuery = `
|
||||||
|
SELECT COUNT(*) as total
|
||||||
|
FROM task_activity_logs
|
||||||
|
WHERE project_id = $1${filterClause}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const [result, countResult] = await Promise.all([
|
||||||
|
db.query(q, [...filterParams, size, offset]),
|
||||||
|
db.query(countQuery, filterType && filterType !== "all" ? [projectId, filterType] : [projectId])
|
||||||
|
]);
|
||||||
|
|
||||||
|
const total = parseInt(countResult.rows[0]?.total || "0");
|
||||||
|
|
||||||
|
// Format the logs
|
||||||
|
for (const log of result.rows) {
|
||||||
|
if (log.attribute_type === "estimation") {
|
||||||
|
log.previous = formatDuration(moment.duration(log.old_value, "minutes"));
|
||||||
|
log.current = formatDuration(moment.duration(log.new_value, "minutes"));
|
||||||
|
} else {
|
||||||
|
log.previous = log.old_value;
|
||||||
|
log.current = log.new_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add color to users
|
||||||
|
if (log.assigned_user) {
|
||||||
|
log.assigned_user.color_code = getColor(log.assigned_user.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (log.done_by) {
|
||||||
|
log.done_by.color_code = getColor(log.done_by.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add default colors for status and priority since table doesn't have color_code
|
||||||
|
if (log.previous_status) {
|
||||||
|
log.previous_status.color_code = "#d9d9d9"; // Default gray color
|
||||||
|
}
|
||||||
|
if (log.next_status) {
|
||||||
|
log.next_status.color_code = "#1890ff"; // Default blue color
|
||||||
|
}
|
||||||
|
if (log.previous_priority) {
|
||||||
|
log.previous_priority.color_code = "#d9d9d9"; // Default gray color
|
||||||
|
}
|
||||||
|
if (log.next_priority) {
|
||||||
|
log.next_priority.color_code = "#ff4d4f"; // Default red color for priority
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate log text
|
||||||
|
log.log_text = await formatLogText(log);
|
||||||
|
log.attribute_type = log.attribute_type?.replace(/_/g, " ");
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = {
|
||||||
|
logs: result.rows,
|
||||||
|
pagination: {
|
||||||
|
current: page,
|
||||||
|
pageSize: size,
|
||||||
|
total,
|
||||||
|
totalPages: Math.ceil(total / size)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return res.status(200).send(new ServerResponse(true, response));
|
||||||
|
} catch (error: any) {
|
||||||
|
console.error("❌ Error in getByProjectId:", error);
|
||||||
|
return res.status(500).send(new ServerResponse(false, null, `Internal server error: ${error.message}`));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@HandleExceptions()
|
||||||
|
public static async exportProjectActivityLogs(req: IWorkLenzRequest, res: IWorkLenzResponse): Promise<void> {
|
||||||
|
// ...keep your export logic as is...
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,291 @@
|
|||||||
|
import { IWorkLenzRequest } from "../interfaces/worklenz-request";
|
||||||
|
import { IWorkLenzResponse } from "../interfaces/worklenz-response";
|
||||||
|
import moment from "moment";
|
||||||
|
import db from "../config/db";
|
||||||
|
import { ServerResponse } from "../models/server-response";
|
||||||
|
import WorklenzControllerBase from "./worklenz-controller-base";
|
||||||
|
import HandleExceptions from "../decorators/handle-exceptions";
|
||||||
|
|
||||||
|
// --- Helpers -------------------------------------------------------------
|
||||||
|
|
||||||
|
function formatDuration(duration: moment.Duration | null): string {
|
||||||
|
if (!duration) return "0m";
|
||||||
|
const hours = Math.floor(duration.asHours());
|
||||||
|
const minutes = duration.minutes();
|
||||||
|
return hours > 0 ? `${hours}h ${minutes}m` : `${minutes}m`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function generateLogText(attributeType: string): string {
|
||||||
|
const map: Record<string,string> = {
|
||||||
|
name: "updated task name",
|
||||||
|
status: "changed status",
|
||||||
|
priority: "changed priority",
|
||||||
|
assignee: "updated assignee",
|
||||||
|
end_date: "changed due date",
|
||||||
|
start_date: "changed start date",
|
||||||
|
estimation: "updated time estimation",
|
||||||
|
description: "updated description",
|
||||||
|
phase: "changed phase",
|
||||||
|
labels: "updated labels",
|
||||||
|
};
|
||||||
|
return map[attributeType] || "made changes to";
|
||||||
|
}
|
||||||
|
|
||||||
|
function isValidUuid(id?: string): boolean {
|
||||||
|
return !!id && /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get a consistent color for a user based on their name
|
||||||
|
function getColorFromName(name: string): string {
|
||||||
|
if (!name) return "#1890ff";
|
||||||
|
|
||||||
|
const colors = [
|
||||||
|
"#f56a00", "#7265e6", "#ffbf00", "#00a2ae",
|
||||||
|
"#1890ff", "#52c41a", "#eb2f96", "#faad14",
|
||||||
|
"#722ed1", "#13c2c2", "#fa8c16", "#a0d911"
|
||||||
|
];
|
||||||
|
|
||||||
|
let hash = 0;
|
||||||
|
for (let i = 0; i < name.length; i++) {
|
||||||
|
hash = name.charCodeAt(i) + ((hash << 5) - hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
return colors[Math.abs(hash) % colors.length];
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Controller ----------------------------------------------------------
|
||||||
|
|
||||||
|
export default class ProjectActivityLogsController extends WorklenzControllerBase {
|
||||||
|
@HandleExceptions()
|
||||||
|
public static async getByProjectId(
|
||||||
|
req: IWorkLenzRequest,
|
||||||
|
res: IWorkLenzResponse
|
||||||
|
): Promise<IWorkLenzResponse> {
|
||||||
|
// 1) Extract & validate inputs
|
||||||
|
const projectId = req.params.id;
|
||||||
|
if (!isValidUuid(projectId)) {
|
||||||
|
return res
|
||||||
|
.status(400)
|
||||||
|
.json({ done: false, body: null, error: "Invalid project ID." });
|
||||||
|
}
|
||||||
|
|
||||||
|
const page = parseInt(req.query.page as string, 10) || 1;
|
||||||
|
const size = parseInt(req.query.size as string, 10) || 20;
|
||||||
|
const offset = (page - 1) * size;
|
||||||
|
const filterType = (req.query.filter as string) || "all";
|
||||||
|
const allowedFilters = [
|
||||||
|
"all","name","status","priority","assignee",
|
||||||
|
"end_date","start_date","estimation","description","phase"
|
||||||
|
];
|
||||||
|
if (!allowedFilters.includes(filterType)) {
|
||||||
|
return res
|
||||||
|
.status(400)
|
||||||
|
.json({ done: false, body: null, error: "Invalid filter type." });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2) Build parameterized SQL
|
||||||
|
let mainQuery = `
|
||||||
|
SELECT
|
||||||
|
tal.id,
|
||||||
|
tal.task_id,
|
||||||
|
tal.user_id,
|
||||||
|
tal.attribute_type,
|
||||||
|
tal.log_type,
|
||||||
|
tal.old_value,
|
||||||
|
tal.new_value,
|
||||||
|
tal.prev_string,
|
||||||
|
tal.next_string,
|
||||||
|
tal.created_at,
|
||||||
|
t.name AS task_name,
|
||||||
|
t.task_no AS task_no,
|
||||||
|
p.key AS project_key,
|
||||||
|
|
||||||
|
-- Include user details directly
|
||||||
|
u.id AS user_id,
|
||||||
|
u.name AS user_name,
|
||||||
|
u.email AS user_email,
|
||||||
|
u.avatar_url AS user_avatar_url
|
||||||
|
FROM task_activity_logs tal
|
||||||
|
LEFT JOIN tasks t ON tal.task_id = t.id
|
||||||
|
LEFT JOIN projects p ON tal.project_id = p.id
|
||||||
|
LEFT JOIN users u ON tal.user_id = u.id
|
||||||
|
WHERE tal.project_id = $1
|
||||||
|
`;
|
||||||
|
|
||||||
|
const queryParams: any[] = [projectId];
|
||||||
|
if (filterType !== "all") {
|
||||||
|
mainQuery += ` AND tal.attribute_type = $2`;
|
||||||
|
queryParams.push(filterType);
|
||||||
|
}
|
||||||
|
mainQuery += ` ORDER BY tal.created_at DESC`;
|
||||||
|
// placeholders for LIMIT / OFFSET
|
||||||
|
const limitIdx = queryParams.length + 1;
|
||||||
|
const offsetIdx = queryParams.length + 2;
|
||||||
|
mainQuery += ` LIMIT $${limitIdx} OFFSET $${offsetIdx}`;
|
||||||
|
queryParams.push(size, offset);
|
||||||
|
|
||||||
|
// Count query
|
||||||
|
const countQuery = `
|
||||||
|
SELECT COUNT(*) AS total
|
||||||
|
FROM task_activity_logs tal
|
||||||
|
WHERE tal.project_id = $1
|
||||||
|
`;
|
||||||
|
const countParams = filterType !== "all"
|
||||||
|
? [projectId, filterType]
|
||||||
|
: [projectId];
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 3) Execute SQL
|
||||||
|
const [dataResult, countResult] = await Promise.all([
|
||||||
|
db.query(mainQuery, queryParams),
|
||||||
|
db.query(
|
||||||
|
countQuery + (filterType !== "all" ? ` AND tal.attribute_type = $2` : ""),
|
||||||
|
countParams
|
||||||
|
),
|
||||||
|
]);
|
||||||
|
|
||||||
|
const total = parseInt(countResult.rows[0]?.total || "0", 10);
|
||||||
|
|
||||||
|
// 4) Transform rows
|
||||||
|
const rows = dataResult.rows;
|
||||||
|
const logs = await Promise.all(rows.map(async (r: any) => {
|
||||||
|
const log: any = { ...r };
|
||||||
|
// Correctly structure user information
|
||||||
|
log.done_by = {
|
||||||
|
id: r.user_id || "",
|
||||||
|
name: r.user_name || "Unknown User",
|
||||||
|
avatar_url: r.user_avatar_url,
|
||||||
|
email: r.user_email || "",
|
||||||
|
color_code: r.user_name ? getColorFromName(r.user_name) : "#1890ff"
|
||||||
|
};
|
||||||
|
// task key
|
||||||
|
log.task_key = r.project_key && r.task_no ?
|
||||||
|
`${r.project_key}-${r.task_no}` :
|
||||||
|
`TASK-${r.task_id?.substring(0, 8) || "unknown"}`;
|
||||||
|
|
||||||
|
// duration / estimation formatting
|
||||||
|
if (log.attribute_type === "estimation") {
|
||||||
|
const oldMin = parseInt(log.old_value, 10);
|
||||||
|
const newMin = parseInt(log.new_value, 10);
|
||||||
|
log.previous = !isNaN(oldMin)
|
||||||
|
? formatDuration(moment.duration(oldMin, "minutes"))
|
||||||
|
: log.old_value;
|
||||||
|
log.current = !isNaN(newMin)
|
||||||
|
? formatDuration(moment.duration(newMin, "minutes"))
|
||||||
|
: log.new_value;
|
||||||
|
} else {
|
||||||
|
log.previous = log.old_value;
|
||||||
|
log.current = log.new_value;
|
||||||
|
}
|
||||||
|
// human‐friendly action
|
||||||
|
log.log_text = generateLogText(r.attribute_type);
|
||||||
|
|
||||||
|
// Handle status changes
|
||||||
|
if (log.attribute_type === "status" && log.old_value && isValidUuid(log.old_value)) {
|
||||||
|
try {
|
||||||
|
const prevStatus = await db.query(
|
||||||
|
`SELECT name, color_code FROM task_statuses WHERE id = $1`,
|
||||||
|
[log.old_value]
|
||||||
|
);
|
||||||
|
if (prevStatus.rows.length > 0) {
|
||||||
|
log.previous_status = {
|
||||||
|
name: prevStatus.rows[0].name,
|
||||||
|
color_code: prevStatus.rows[0].color_code || "#d9d9d9"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Error fetching previous status:", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (log.attribute_type === "status" && log.new_value && isValidUuid(log.new_value)) {
|
||||||
|
try {
|
||||||
|
const nextStatus = await db.query(
|
||||||
|
`SELECT name, color_code FROM task_statuses WHERE id = $1`,
|
||||||
|
[log.new_value]
|
||||||
|
);
|
||||||
|
if (nextStatus.rows.length > 0) {
|
||||||
|
log.next_status = {
|
||||||
|
name: nextStatus.rows[0].name,
|
||||||
|
color_code: nextStatus.rows[0].color_code || "#1890ff"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Error fetching next status:", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Handle priority changes
|
||||||
|
if (log.attribute_type === "priority" && log.old_value && isValidUuid(log.old_value)) {
|
||||||
|
try {
|
||||||
|
const prevPriority = await db.query(
|
||||||
|
`SELECT name, color_code FROM task_priorities WHERE id = $1`,
|
||||||
|
[log.old_value]
|
||||||
|
);
|
||||||
|
if (prevPriority.rows.length > 0) {
|
||||||
|
log.previous_priority = {
|
||||||
|
name: prevPriority.rows[0].name,
|
||||||
|
color_code: prevPriority.rows[0].color_code || "#d9d9d9"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Error fetching previous priority:", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (log.attribute_type === "priority" && log.new_value && isValidUuid(log.new_value)) {
|
||||||
|
try {
|
||||||
|
const nextPriority = await db.query(
|
||||||
|
`SELECT name, color_code FROM task_priorities WHERE id = $1`,
|
||||||
|
[log.new_value]
|
||||||
|
);
|
||||||
|
if (nextPriority.rows.length > 0) {
|
||||||
|
log.next_priority = {
|
||||||
|
name: nextPriority.rows[0].name,
|
||||||
|
color_code: nextPriority.rows[0].color_code || "#ff4d4f"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Error fetching next priority:", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Handle assignee changes
|
||||||
|
if (log.attribute_type === "assignee" && log.new_value && isValidUuid(log.new_value)) {
|
||||||
|
try {
|
||||||
|
const assignedUser = await db.query(
|
||||||
|
`SELECT id, name, avatar_url, email FROM users WHERE id = $1`,
|
||||||
|
[log.new_value]
|
||||||
|
);
|
||||||
|
if (assignedUser.rows.length > 0) {
|
||||||
|
log.assigned_user = {
|
||||||
|
...assignedUser.rows[0],
|
||||||
|
color_code: getColorFromName(assignedUser.rows[0].name)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Error fetching assigned user:", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return log;
|
||||||
|
}));
|
||||||
|
|
||||||
|
// 5) Send back a clean response
|
||||||
|
const response = {
|
||||||
|
logs,
|
||||||
|
pagination: {
|
||||||
|
current: page,
|
||||||
|
pageSize: size,
|
||||||
|
total,
|
||||||
|
totalPages: Math.ceil(total / size),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return res.status(200).send(new ServerResponse(true, response));
|
||||||
|
} catch (err) {
|
||||||
|
console.error("🔥 getByProjectId error:", err);
|
||||||
|
return res.status(500).send(
|
||||||
|
new ServerResponse(false, null, "Internal server error fetching logs.")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,5 +7,7 @@ import safeControllerFunction from "../../shared/safe-controller-function";
|
|||||||
const activityLogsApiRouter = express.Router();
|
const activityLogsApiRouter = express.Router();
|
||||||
|
|
||||||
activityLogsApiRouter.get("/:id", idParamValidator, safeControllerFunction(ActivitylogsController.get));
|
activityLogsApiRouter.get("/:id", idParamValidator, safeControllerFunction(ActivitylogsController.get));
|
||||||
|
activityLogsApiRouter.get("/project/:id", idParamValidator, safeControllerFunction(ActivitylogsController.getByProjectId));
|
||||||
|
activityLogsApiRouter.get("/project/:id/export", safeControllerFunction(ActivitylogsController.exportProjectActivityLogs));
|
||||||
|
|
||||||
export default activityLogsApiRouter;
|
export default activityLogsApiRouter;
|
||||||
|
|||||||
@@ -1,120 +1,126 @@
|
|||||||
import express from "express";
|
import express from "express";
|
||||||
|
|
||||||
import AccessControlsController from "../../controllers/access-controls-controller";
|
import AccessControlsController from "../../controllers/access-controls-controller";
|
||||||
import AuthController from "../../controllers/auth-controller";
|
import AuthController from "../../controllers/auth-controller";
|
||||||
import LogsController from "../../controllers/logs-controller";
|
import LogsController from "../../controllers/logs-controller";
|
||||||
import OverviewController from "../../controllers/overview-controller";
|
import OverviewController from "../../controllers/overview-controller";
|
||||||
import TaskPrioritiesController from "../../controllers/task-priorities-controller";
|
import TaskPrioritiesController from "../../controllers/task-priorities-controller";
|
||||||
|
|
||||||
import attachmentsApiRouter from "./attachments-api-router";
|
import attachmentsApiRouter from "./attachments-api-router";
|
||||||
import clientsApiRouter from "./clients-api-router";
|
import clientsApiRouter from "./clients-api-router";
|
||||||
import jobTitlesApiRouter from "./job-titles-api-router";
|
import jobTitlesApiRouter from "./job-titles-api-router";
|
||||||
import notificationsApiRouter from "./notifications-api-router";
|
import notificationsApiRouter from "./notifications-api-router";
|
||||||
import personalOverviewApiRouter from "./personal-overview-api-router";
|
import personalOverviewApiRouter from "./personal-overview-api-router";
|
||||||
import projectMembersApiRouter from "./project-members-api-router";
|
import projectMembersApiRouter from "./project-members-api-router";
|
||||||
import projectsApiRouter from "./projects-api-router";
|
import projectsApiRouter from "./projects-api-router";
|
||||||
import settingsApiRouter from "./settings-api-router";
|
import settingsApiRouter from "./settings-api-router";
|
||||||
import statusesApiRouter from "./statuses-api-router";
|
import statusesApiRouter from "./statuses-api-router";
|
||||||
import subTasksApiRouter from "./sub-tasks-api-router";
|
import subTasksApiRouter from "./sub-tasks-api-router";
|
||||||
import taskCommentsApiRouter from "./task-comments-api-router";
|
import taskCommentsApiRouter from "./task-comments-api-router";
|
||||||
import taskWorkLogApiRouter from "./task-work-log-api-router";
|
import taskWorkLogApiRouter from "./task-work-log-api-router";
|
||||||
import tasksApiRouter from "./tasks-api-router";
|
import tasksApiRouter from "./tasks-api-router";
|
||||||
import teamMembersApiRouter from "./team-members-api-router";
|
import teamMembersApiRouter from "./team-members-api-router";
|
||||||
import teamsApiRouter from "./teams-api-router";
|
import teamsApiRouter from "./teams-api-router";
|
||||||
import timezonesApiRouter from "./timezones-api-router";
|
import timezonesApiRouter from "./timezones-api-router";
|
||||||
import todoListApiRouter from "./todo-list-api-router";
|
import todoListApiRouter from "./todo-list-api-router";
|
||||||
import projectStatusesApiRouter from "./project-statuses-api-router";
|
import projectStatusesApiRouter from "./project-statuses-api-router";
|
||||||
import labelsApiRouter from "./labels-api-router";
|
import labelsApiRouter from "./labels-api-router";
|
||||||
import sharedProjectsApiRouter from "./shared-projects-api-router";
|
import sharedProjectsApiRouter from "./shared-projects-api-router";
|
||||||
import resourceAllocationApiRouter from "./resource-allocation-api-router";
|
import resourceAllocationApiRouter from "./resource-allocation-api-router";
|
||||||
import taskTemplatesApiRouter from "./task-templates-api-router";
|
import taskTemplatesApiRouter from "./task-templates-api-router";
|
||||||
import projectInsightsApiRouter from "./project-insights-api-router";
|
import projectInsightsApiRouter from "./project-insights-api-router";
|
||||||
import passwordValidator from "../../middlewares/validators/password-validator";
|
import passwordValidator from "../../middlewares/validators/password-validator";
|
||||||
import adminCenterApiRouter from "./admin-center-api-router";
|
import adminCenterApiRouter from "./admin-center-api-router";
|
||||||
import reportingApiRouter from "./reporting-api-router";
|
import reportingApiRouter from "./reporting-api-router";
|
||||||
import activityLogsApiRouter from "./activity-logs-api-router";
|
import activityLogsApiRouter from "./activity-logs-api-router";
|
||||||
import safeControllerFunction from "../../shared/safe-controller-function";
|
import safeControllerFunction from "../../shared/safe-controller-function";
|
||||||
import projectFoldersApiRouter from "./project-folders-api-router";
|
import projectFoldersApiRouter from "./project-folders-api-router";
|
||||||
import taskPhasesApiRouter from "./task-phases-api-router";
|
import taskPhasesApiRouter from "./task-phases-api-router";
|
||||||
import projectCategoriesApiRouter from "./project-categories-api-router";
|
import projectCategoriesApiRouter from "./project-categories-api-router";
|
||||||
import homePageApiRouter from "./home-page-api-router";
|
import homePageApiRouter from "./home-page-api-router";
|
||||||
import ganttApiRouter from "./gantt-api-router";
|
import ganttApiRouter from "./gantt-api-router";
|
||||||
import projectCommentsApiRouter from "./project-comments-api-router";
|
import projectCommentsApiRouter from "./project-comments-api-router";
|
||||||
import reportingExportApiRouter from "./reporting-export-api-router";
|
import reportingExportApiRouter from "./reporting-export-api-router";
|
||||||
import projectHealthsApiRouter from "./project-healths-api-router";
|
import projectHealthsApiRouter from "./project-healths-api-router";
|
||||||
import ptTasksApiRouter from "./pt-tasks-api-router";
|
import ptTasksApiRouter from "./pt-tasks-api-router";
|
||||||
import projectTemplatesApiRouter from "./project-templates-api";
|
import projectTemplatesApiRouter from "./project-templates-api";
|
||||||
import ptTaskPhasesApiRouter from "./pt_task-phases-api-router";
|
import ptTaskPhasesApiRouter from "./pt_task-phases-api-router";
|
||||||
import ptStatusesApiRouter from "./pt-statuses-api-router";
|
import ptStatusesApiRouter from "./pt-statuses-api-router";
|
||||||
import workloadApiRouter from "./gannt-apis/workload-api-router";
|
import workloadApiRouter from "./gannt-apis/workload-api-router";
|
||||||
import roadmapApiRouter from "./gannt-apis/roadmap-api-router";
|
import roadmapApiRouter from "./gannt-apis/roadmap-api-router";
|
||||||
import scheduleApiRouter from "./gannt-apis/schedule-api-router";
|
import scheduleApiRouter from "./gannt-apis/schedule-api-router";
|
||||||
import scheduleApiV2Router from "./gannt-apis/schedule-api-v2-router";
|
import scheduleApiV2Router from "./gannt-apis/schedule-api-v2-router";
|
||||||
import projectManagerApiRouter from "./project-managers-api-router";
|
import projectManagerApiRouter from "./project-managers-api-router";
|
||||||
|
|
||||||
import billingApiRouter from "./billing-api-router";
|
import billingApiRouter from "./billing-api-router";
|
||||||
import taskDependenciesApiRouter from "./task-dependencies-api-router";
|
import taskDependenciesApiRouter from "./task-dependencies-api-router";
|
||||||
|
|
||||||
import taskRecurringApiRouter from "./task-recurring-api-router";
|
import taskRecurringApiRouter from "./task-recurring-api-router";
|
||||||
|
|
||||||
|
import customColumnsApiRouter from "./custom-columns-api-router";
|
||||||
|
|
||||||
|
import projectActivityLogsApiRouter from "./project-activity-logs-api-router";
|
||||||
|
|
||||||
|
const api = express.Router();
|
||||||
|
|
||||||
|
api.use("/projects", projectsApiRouter);
|
||||||
|
api.use("/team-members", teamMembersApiRouter);
|
||||||
|
api.use("/job-titles", jobTitlesApiRouter);
|
||||||
|
api.use("/clients", clientsApiRouter);
|
||||||
|
api.use("/teams", teamsApiRouter);
|
||||||
|
api.use("/tasks", tasksApiRouter);
|
||||||
|
api.use("/settings", settingsApiRouter);
|
||||||
|
api.use("/personal-overview", personalOverviewApiRouter);
|
||||||
|
api.use("/statuses", statusesApiRouter);
|
||||||
|
api.use("/todo-list", todoListApiRouter);
|
||||||
|
api.use("/notifications", notificationsApiRouter);
|
||||||
|
api.use("/attachments", attachmentsApiRouter);
|
||||||
|
api.use("/sub-tasks", subTasksApiRouter);
|
||||||
|
api.use("/project-members", projectMembersApiRouter);
|
||||||
|
api.use("/task-time-log", taskWorkLogApiRouter);
|
||||||
|
api.use("/task-comments", taskCommentsApiRouter);
|
||||||
|
api.use("/timezones", timezonesApiRouter);
|
||||||
|
api.use("/project-statuses", projectStatusesApiRouter);
|
||||||
|
api.use("/labels", labelsApiRouter);
|
||||||
|
api.use("/resource-allocation", resourceAllocationApiRouter);
|
||||||
|
api.use("/shared/projects", sharedProjectsApiRouter);
|
||||||
|
api.use("/task-templates", taskTemplatesApiRouter);
|
||||||
|
api.use("/project-insights", projectInsightsApiRouter);
|
||||||
|
api.use("/admin-center", adminCenterApiRouter);
|
||||||
|
api.use("/reporting", reportingApiRouter);
|
||||||
|
api.use("/activity-logs", activityLogsApiRouter);
|
||||||
|
api.use("/projects-folders", projectFoldersApiRouter);
|
||||||
|
api.use("/task-phases", taskPhasesApiRouter);
|
||||||
|
api.use("/project-categories", projectCategoriesApiRouter);
|
||||||
|
api.use("/home", homePageApiRouter);
|
||||||
|
api.use("/gantt", ganttApiRouter);
|
||||||
|
api.use("/project-comments", projectCommentsApiRouter);
|
||||||
|
api.use("/reporting-export", reportingExportApiRouter);
|
||||||
|
api.use("/project-healths", projectHealthsApiRouter);
|
||||||
|
api.use("/project-templates", projectTemplatesApiRouter);
|
||||||
|
api.use("/pt-tasks", ptTasksApiRouter);
|
||||||
|
api.use("/pt-task-phases", ptTaskPhasesApiRouter);
|
||||||
|
api.use("/pt-statuses", ptStatusesApiRouter);
|
||||||
|
api.use("/workload-gannt", workloadApiRouter);
|
||||||
|
api.use("/roadmap-gannt", roadmapApiRouter);
|
||||||
|
api.use("/schedule-gannt", scheduleApiRouter);
|
||||||
|
api.use("/schedule-gannt-v2", scheduleApiV2Router);
|
||||||
|
api.use("/project-managers", projectManagerApiRouter);
|
||||||
|
|
||||||
|
api.get("/overview/:id", safeControllerFunction(OverviewController.getById));
|
||||||
|
api.get("/task-priorities", safeControllerFunction(TaskPrioritiesController.get));
|
||||||
|
api.post("/change-password", passwordValidator, safeControllerFunction(AuthController.changePassword));
|
||||||
|
api.get("/access-controls/roles", safeControllerFunction(AccessControlsController.getRoles));
|
||||||
|
api.get("/logs/my-dashboard", safeControllerFunction(LogsController.getActivityLog));
|
||||||
|
|
||||||
|
api.use("/billing", billingApiRouter);
|
||||||
|
api.use("/task-dependencies", taskDependenciesApiRouter);
|
||||||
|
|
||||||
|
api.use("/task-recurring", taskRecurringApiRouter);
|
||||||
|
|
||||||
|
|
||||||
const api = express.Router();
|
|
||||||
|
|
||||||
api.use("/projects", projectsApiRouter);
|
|
||||||
api.use("/team-members", teamMembersApiRouter);
|
|
||||||
api.use("/job-titles", jobTitlesApiRouter);
|
|
||||||
api.use("/clients", clientsApiRouter);
|
|
||||||
api.use("/teams", teamsApiRouter);
|
|
||||||
api.use("/tasks", tasksApiRouter);
|
|
||||||
api.use("/settings", settingsApiRouter);
|
|
||||||
api.use("/personal-overview", personalOverviewApiRouter);
|
|
||||||
api.use("/statuses", statusesApiRouter);
|
|
||||||
api.use("/todo-list", todoListApiRouter);
|
|
||||||
api.use("/notifications", notificationsApiRouter);
|
|
||||||
api.use("/attachments", attachmentsApiRouter);
|
|
||||||
api.use("/sub-tasks", subTasksApiRouter);
|
|
||||||
api.use("/project-members", projectMembersApiRouter);
|
|
||||||
api.use("/task-time-log", taskWorkLogApiRouter);
|
|
||||||
api.use("/task-comments", taskCommentsApiRouter);
|
|
||||||
api.use("/timezones", timezonesApiRouter);
|
|
||||||
api.use("/project-statuses", projectStatusesApiRouter);
|
|
||||||
api.use("/labels", labelsApiRouter);
|
|
||||||
api.use("/resource-allocation", resourceAllocationApiRouter);
|
|
||||||
api.use("/shared/projects", sharedProjectsApiRouter);
|
|
||||||
api.use("/task-templates", taskTemplatesApiRouter);
|
|
||||||
api.use("/project-insights", projectInsightsApiRouter);
|
|
||||||
api.use("/admin-center", adminCenterApiRouter);
|
|
||||||
api.use("/reporting", reportingApiRouter);
|
|
||||||
api.use("/activity-logs", activityLogsApiRouter);
|
|
||||||
api.use("/projects-folders", projectFoldersApiRouter);
|
|
||||||
api.use("/task-phases", taskPhasesApiRouter);
|
|
||||||
api.use("/project-categories", projectCategoriesApiRouter);
|
|
||||||
api.use("/home", homePageApiRouter);
|
|
||||||
api.use("/gantt", ganttApiRouter);
|
|
||||||
api.use("/project-comments", projectCommentsApiRouter);
|
|
||||||
api.use("/reporting-export", reportingExportApiRouter);
|
|
||||||
api.use("/project-healths", projectHealthsApiRouter);
|
|
||||||
api.use("/project-templates", projectTemplatesApiRouter);
|
|
||||||
api.use("/pt-tasks", ptTasksApiRouter);
|
|
||||||
api.use("/pt-task-phases", ptTaskPhasesApiRouter);
|
|
||||||
api.use("/pt-statuses", ptStatusesApiRouter);
|
|
||||||
api.use("/workload-gannt", workloadApiRouter);
|
|
||||||
api.use("/roadmap-gannt", roadmapApiRouter);
|
|
||||||
api.use("/schedule-gannt", scheduleApiRouter);
|
|
||||||
api.use("/schedule-gannt-v2", scheduleApiV2Router);
|
|
||||||
api.use("/project-managers", projectManagerApiRouter);
|
|
||||||
|
|
||||||
api.get("/overview/:id", safeControllerFunction(OverviewController.getById));
|
|
||||||
api.get("/task-priorities", safeControllerFunction(TaskPrioritiesController.get));
|
|
||||||
api.post("/change-password", passwordValidator, safeControllerFunction(AuthController.changePassword));
|
|
||||||
api.get("/access-controls/roles", safeControllerFunction(AccessControlsController.getRoles));
|
|
||||||
api.get("/logs/my-dashboard", safeControllerFunction(LogsController.getActivityLog));
|
|
||||||
|
|
||||||
api.use("/billing", billingApiRouter);
|
|
||||||
api.use("/task-dependencies", taskDependenciesApiRouter);
|
|
||||||
|
|
||||||
api.use("/task-recurring", taskRecurringApiRouter);
|
|
||||||
api.use("/custom-columns", customColumnsApiRouter);
|
api.use("/custom-columns", customColumnsApiRouter);
|
||||||
|
api.use("/activity-logs", activityLogsApiRouter);
|
||||||
|
|
||||||
|
api.use("/project-activity-logs", projectActivityLogsApiRouter);
|
||||||
|
|
||||||
|
export default api;
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
import express from "express";
|
||||||
|
import idParamValidator from "../../middlewares/validators/id-param-validator";
|
||||||
|
import safeControllerFunction from "../../shared/safe-controller-function";
|
||||||
|
import ProjectActivityLogsController from "../../controllers/project-activity-logs-controller";
|
||||||
|
|
||||||
|
const projectActivityLogsApiRouter = express.Router();
|
||||||
|
|
||||||
|
// Match the client URL (/project/:projectId) and ensure param name lines up
|
||||||
|
projectActivityLogsApiRouter.get(
|
||||||
|
"/project/:id",
|
||||||
|
// Validate that id is a proper UUID
|
||||||
|
idParamValidator,
|
||||||
|
// Wrap the controller to catch and forward errors
|
||||||
|
safeControllerFunction(ProjectActivityLogsController.getByProjectId)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Optional health‐check endpoint
|
||||||
|
projectActivityLogsApiRouter.get("/test", (req, res) => {
|
||||||
|
res.json({ message: "Project activity logs router is working!" });
|
||||||
|
});
|
||||||
|
|
||||||
|
export default projectActivityLogsApiRouter;
|
||||||
6
worklenz-frontend/.dockerignore
Normal file
6
worklenz-frontend/.dockerignore
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
node_modules
|
||||||
|
npm-debug.log
|
||||||
|
.git
|
||||||
|
.gitignore
|
||||||
|
.dockerignore
|
||||||
|
README.md
|
||||||
@@ -12,7 +12,7 @@ COPY . .
|
|||||||
RUN echo "window.VITE_API_URL='${VITE_API_URL:-http://backend:3000}';" > ./public/env-config.js && \
|
RUN echo "window.VITE_API_URL='${VITE_API_URL:-http://backend:3000}';" > ./public/env-config.js && \
|
||||||
echo "window.VITE_SOCKET_URL='${VITE_SOCKET_URL:-ws://backend:3000}';" >> ./public/env-config.js
|
echo "window.VITE_SOCKET_URL='${VITE_SOCKET_URL:-ws://backend:3000}';" >> ./public/env-config.js
|
||||||
|
|
||||||
RUN npm run build
|
RUN NODE_OPTIONS="--max-old-space-size=4096" npm run build
|
||||||
|
|
||||||
FROM node:22-alpine AS production
|
FROM node:22-alpine AS production
|
||||||
|
|
||||||
@@ -23,13 +23,20 @@ RUN npm install -g serve
|
|||||||
COPY --from=build /app/build /app/build
|
COPY --from=build /app/build /app/build
|
||||||
COPY --from=build /app/public/env-config.js /app/build/env-config.js
|
COPY --from=build /app/public/env-config.js /app/build/env-config.js
|
||||||
|
|
||||||
|
# Create env-config.sh script
|
||||||
|
RUN echo '#!/bin/sh' > /app/env-config.sh && \
|
||||||
|
echo '# Update env-config.js with runtime environment variables' >> /app/env-config.sh && \
|
||||||
|
echo 'cat > /app/build/env-config.js << EOL' >> /app/env-config.sh && \
|
||||||
|
echo 'window.VITE_API_URL="${VITE_API_URL:-http://backend:3000}";' >> /app/env-config.sh && \
|
||||||
|
echo 'window.VITE_SOCKET_URL="${VITE_SOCKET_URL:-ws://backend:3000}";' >> /app/env-config.sh && \
|
||||||
|
echo 'EOL' >> /app/env-config.sh && \
|
||||||
|
chmod +x /app/env-config.sh
|
||||||
|
|
||||||
# Create start.sh script
|
# Create start.sh script
|
||||||
RUN echo '#!/bin/sh' > /app/start.sh && \
|
RUN echo '#!/bin/sh' > /app/start.sh && \
|
||||||
echo '# Update env-config.js with runtime environment variables' >> /app/start.sh && \
|
echo '# Run environment configuration' >> /app/start.sh && \
|
||||||
echo 'cat > /app/build/env-config.js << EOL' >> /app/start.sh && \
|
echo '/app/env-config.sh' >> /app/start.sh && \
|
||||||
echo 'window.VITE_API_URL="${VITE_API_URL:-http://backend:3000}";' >> /app/start.sh && \
|
echo '# Start the server' >> /app/start.sh && \
|
||||||
echo 'window.VITE_SOCKET_URL="${VITE_SOCKET_URL:-ws://backend:3000}";' >> /app/start.sh && \
|
|
||||||
echo 'EOL' >> /app/start.sh && \
|
|
||||||
echo 'exec serve -s build -l 5000' >> /app/start.sh && \
|
echo 'exec serve -s build -l 5000' >> /app/start.sh && \
|
||||||
chmod +x /app/start.sh
|
chmod +x /app/start.sh
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ Worklenz is a project management application built with React, TypeScript, and A
|
|||||||
- [Project Structure](#project-structure)
|
- [Project Structure](#project-structure)
|
||||||
- [Contributing](#contributing)
|
- [Contributing](#contributing)
|
||||||
- [Learn More](#learn-more)
|
- [Learn More](#learn-more)
|
||||||
|
- [License](#license)
|
||||||
|
|
||||||
## Getting Started
|
## Getting Started
|
||||||
|
|
||||||
@@ -93,3 +94,7 @@ To learn more about the technologies used in this project:
|
|||||||
- [TypeScript Documentation](https://www.typescriptlang.org/docs/)
|
- [TypeScript Documentation](https://www.typescriptlang.org/docs/)
|
||||||
- [Ant Design Documentation](https://ant.design/docs/react/introduce)
|
- [Ant Design Documentation](https://ant.design/docs/react/introduce)
|
||||||
- [Vite Documentation](https://vitejs.dev/guide/)
|
- [Vite Documentation](https://vitejs.dev/guide/)
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
Worklenz is open source and released under the [GNU Affero General Public License Version 3 (AGPLv3)](LICENSE).
|
||||||
|
|||||||
@@ -14,6 +14,81 @@
|
|||||||
<title>Worklenz</title>
|
<title>Worklenz</title>
|
||||||
<!-- Environment configuration -->
|
<!-- Environment configuration -->
|
||||||
<script src="/env-config.js"></script>
|
<script src="/env-config.js"></script>
|
||||||
|
<!-- Google Analytics -->
|
||||||
|
<script>
|
||||||
|
// Function to initialize Google Analytics
|
||||||
|
function initGoogleAnalytics() {
|
||||||
|
// Load the Google Analytics script
|
||||||
|
const script = document.createElement('script');
|
||||||
|
script.async = true;
|
||||||
|
|
||||||
|
// Determine which tracking ID to use based on the environment
|
||||||
|
const isProduction = window.location.hostname === 'worklenz.com' ||
|
||||||
|
window.location.hostname === 'app.worklenz.com';
|
||||||
|
|
||||||
|
const trackingId = isProduction
|
||||||
|
? 'G-XXXXXXXXXX'
|
||||||
|
: 'G-3LM2HGWEXG'; // Open source tracking ID
|
||||||
|
|
||||||
|
script.src = `https://www.googletagmanager.com/gtag/js?id=${trackingId}`;
|
||||||
|
document.head.appendChild(script);
|
||||||
|
|
||||||
|
// Initialize Google Analytics
|
||||||
|
window.dataLayer = window.dataLayer || [];
|
||||||
|
function gtag(){dataLayer.push(arguments);}
|
||||||
|
gtag('js', new Date());
|
||||||
|
gtag('config', trackingId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize analytics
|
||||||
|
initGoogleAnalytics();
|
||||||
|
|
||||||
|
// Function to show privacy notice
|
||||||
|
function showPrivacyNotice() {
|
||||||
|
const notice = document.createElement('div');
|
||||||
|
notice.style.cssText = `
|
||||||
|
position: fixed;
|
||||||
|
bottom: 16px;
|
||||||
|
right: 16px;
|
||||||
|
background: #222;
|
||||||
|
color: #f5f5f5;
|
||||||
|
padding: 12px 16px 10px 16px;
|
||||||
|
border-radius: 7px;
|
||||||
|
box-shadow: 0 2px 8px rgba(0,0,0,0.18);
|
||||||
|
z-index: 1000;
|
||||||
|
max-width: 320px;
|
||||||
|
font-family: Inter, sans-serif;
|
||||||
|
border: 1px solid #333;
|
||||||
|
font-size: 0.95rem;
|
||||||
|
`;
|
||||||
|
notice.innerHTML = `
|
||||||
|
<div style="margin-bottom: 6px; font-weight: 600; color: #fff; font-size: 1rem;">Analytics Notice</div>
|
||||||
|
<div style="margin-bottom: 8px; color: #f5f5f5;">This app uses Google Analytics for anonymous usage stats. No personal data is tracked.</div>
|
||||||
|
<button id="analytics-notice-btn" style="padding: 5px 14px; background: #1890ff; color: white; border: none; border-radius: 3px; cursor: pointer; font-size: 0.95rem;">Got it</button>
|
||||||
|
`;
|
||||||
|
document.body.appendChild(notice);
|
||||||
|
// Add event listener to button
|
||||||
|
const btn = notice.querySelector('#analytics-notice-btn');
|
||||||
|
btn.addEventListener('click', function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
localStorage.setItem('privacyNoticeShown', 'true');
|
||||||
|
notice.remove();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for DOM to be ready
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
// Check if we should show the notice
|
||||||
|
const isProduction = window.location.hostname === 'worklenz.com' ||
|
||||||
|
window.location.hostname === 'app.worklenz.com';
|
||||||
|
const noticeShown = localStorage.getItem('privacyNoticeShown') === 'true';
|
||||||
|
|
||||||
|
// Show notice if not in production and not shown before
|
||||||
|
if (!isProduction && !noticeShown) {
|
||||||
|
showPrivacyNotice();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
|||||||
22
worklenz-frontend/package-lock.json
generated
22
worklenz-frontend/package-lock.json
generated
@@ -63,6 +63,7 @@
|
|||||||
"@types/node": "^20.8.4",
|
"@types/node": "^20.8.4",
|
||||||
"@types/react": "19.0.0",
|
"@types/react": "19.0.0",
|
||||||
"@types/react-dom": "19.0.0",
|
"@types/react-dom": "19.0.0",
|
||||||
|
"@types/react-window-infinite-loader": "^1.0.9",
|
||||||
"@vitejs/plugin-react": "^4.3.4",
|
"@vitejs/plugin-react": "^4.3.4",
|
||||||
"autoprefixer": "^10.4.20",
|
"autoprefixer": "^10.4.20",
|
||||||
"postcss": "^8.5.2",
|
"postcss": "^8.5.2",
|
||||||
@@ -2449,6 +2450,27 @@
|
|||||||
"@types/react": "*"
|
"@types/react": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/react-window": {
|
||||||
|
"version": "1.8.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/react-window/-/react-window-1.8.8.tgz",
|
||||||
|
"integrity": "sha512-8Ls660bHR1AUA2kuRvVG9D/4XpRC6wjAaPT9dil7Ckc76eP9TKWZwwmgfq8Q1LANX3QNDnoU4Zp48A3w+zK69Q==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/react": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@types/react-window-infinite-loader": {
|
||||||
|
"version": "1.0.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/react-window-infinite-loader/-/react-window-infinite-loader-1.0.9.tgz",
|
||||||
|
"integrity": "sha512-gEInTjQwURCnDOFyIEK2+fWB5gTjqwx30O62QfxA9stE5aiB6EWkGj4UMhc0axq7/FV++Gs/TGW8FtgEx0S6Tw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"@types/react-window": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@types/trusted-types": {
|
"node_modules/@types/trusted-types": {
|
||||||
"version": "2.0.7",
|
"version": "2.0.7",
|
||||||
"resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz",
|
"resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz",
|
||||||
|
|||||||
@@ -66,6 +66,7 @@
|
|||||||
"@types/node": "^20.8.4",
|
"@types/node": "^20.8.4",
|
||||||
"@types/react": "19.0.0",
|
"@types/react": "19.0.0",
|
||||||
"@types/react-dom": "19.0.0",
|
"@types/react-dom": "19.0.0",
|
||||||
|
"@types/react-window-infinite-loader": "^1.0.9",
|
||||||
"@vitejs/plugin-react": "^4.3.4",
|
"@vitejs/plugin-react": "^4.3.4",
|
||||||
"autoprefixer": "^10.4.20",
|
"autoprefixer": "^10.4.20",
|
||||||
"postcss": "^8.5.2",
|
"postcss": "^8.5.2",
|
||||||
|
|||||||
4
worklenz-frontend/public/locales/alb/404-page.json
Normal file
4
worklenz-frontend/public/locales/alb/404-page.json
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"doesNotExistText": "Na vjen keq, faqja që kërkoni nuk ekziston.",
|
||||||
|
"backHomeButton": "Kthehu në Faqen Kryesore"
|
||||||
|
}
|
||||||
31
worklenz-frontend/public/locales/alb/account-setup.json
Normal file
31
worklenz-frontend/public/locales/alb/account-setup.json
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
{
|
||||||
|
"continue": "Vazhdo",
|
||||||
|
|
||||||
|
"setupYourAccount": "Konfiguro Llogarinë Tënde në Worklenz.",
|
||||||
|
"organizationStepTitle": "Emërtoni Organizatën Tuaj",
|
||||||
|
"organizationStepLabel": "Zgjidhni një emër për llogarinë tuaj në Worklenz.",
|
||||||
|
|
||||||
|
"projectStepTitle": "Krijoni projektin tuaj të parë",
|
||||||
|
"projectStepLabel": "Në cilin projekt po punoni aktualisht?",
|
||||||
|
"projectStepPlaceholder": "p.sh. Plani i Marketingut",
|
||||||
|
|
||||||
|
"tasksStepTitle": "Krijoni detyrat tuaja të para",
|
||||||
|
"tasksStepLabel": "Shkruani disa detyra që do të kryeni në",
|
||||||
|
"tasksStepAddAnother": "Shto një tjetër",
|
||||||
|
|
||||||
|
"emailPlaceholder": "Adresa email",
|
||||||
|
"invalidEmail": "Ju lutemi vendosni një adresë email të vlefshme",
|
||||||
|
"or": "ose",
|
||||||
|
"templateButton": "Importo nga shablloni",
|
||||||
|
"goBack": "Kthehu Mbrapa",
|
||||||
|
"cancel": "Anulo",
|
||||||
|
"create": "Krijo",
|
||||||
|
"templateDrawerTitle": "Zgjidh nga shabllonet",
|
||||||
|
"step3InputLabel": "Fto me email",
|
||||||
|
"addAnother": "Shto një tjetër",
|
||||||
|
"skipForNow": "Kalo tani për tani",
|
||||||
|
"formTitle": "Krijoni detyrën tuaj të parë.",
|
||||||
|
"step3Title": "Fto ekipin tënd të punojë me",
|
||||||
|
"maxMembers": " (Mund të ftoni deri në 5 anëtarë)",
|
||||||
|
"maxTasks": " (Mund të krijoni deri në 5 detyra)"
|
||||||
|
}
|
||||||
@@ -0,0 +1,113 @@
|
|||||||
|
{
|
||||||
|
"title": "Faturimet",
|
||||||
|
"currentBill": "Fatura Aktuale",
|
||||||
|
"configuration": "Konfigurimi",
|
||||||
|
"currentPlanDetails": "Detajet e Planit Aktual",
|
||||||
|
"upgradePlan": "Përmirëso Planin",
|
||||||
|
"cardBodyText01": "Provë falas",
|
||||||
|
"cardBodyText02": "(Plani juaj i provës skadon në 1 muaj 19 ditë)",
|
||||||
|
"redeemCode": "Kodi i Zbritjes",
|
||||||
|
"accountStorage": "Depozita e Llogarisë",
|
||||||
|
"used": "Përdorur:",
|
||||||
|
"remaining": "E mbetur:",
|
||||||
|
"charges": "Tarifat",
|
||||||
|
"tooltip": "Tarifat për ciklin aktual të faturimit",
|
||||||
|
"description": "Përshkrimi",
|
||||||
|
"billingPeriod": "Periudha e Faturimit",
|
||||||
|
"billStatus": "Statusi i Faturës",
|
||||||
|
"perUserValue": "Vlera për Përdorues",
|
||||||
|
"users": "Përdoruesit",
|
||||||
|
|
||||||
|
"amount": "Shuma",
|
||||||
|
"invoices": "Faturat",
|
||||||
|
"transactionId": "ID e Transaksionit",
|
||||||
|
"transactionDate": "Data e Transaksionit",
|
||||||
|
"paymentMethod": "Metoda e Pagesës",
|
||||||
|
"status": "Statusi",
|
||||||
|
"ltdUsers": "Mund të shtoni deri në {{ltd_users}} përdorues.",
|
||||||
|
|
||||||
|
"totalSeats": "Vende totale",
|
||||||
|
"availableSeats": "Vende të disponueshme",
|
||||||
|
"addMoreSeats": "Shto më shumë vende",
|
||||||
|
|
||||||
|
"drawerTitle": "Kodi i Zbritjes",
|
||||||
|
"label": "Kodi i Zbritjes",
|
||||||
|
"drawerPlaceholder": "Vendosni kodin tuaj të zbritjes",
|
||||||
|
"redeemSubmit": "Paraqit",
|
||||||
|
|
||||||
|
"modalTitle": "Zgjidhni planin më të mirë për ekipin tuaj",
|
||||||
|
"seatLabel": "Numri i vendeve",
|
||||||
|
"freePlan": "Plan Falas",
|
||||||
|
"startup": "Startup",
|
||||||
|
"business": "Biznes",
|
||||||
|
"tag": "Më i Popullarizuar",
|
||||||
|
"enterprise": "Ndërmarrje",
|
||||||
|
|
||||||
|
"freeSubtitle": "falas përgjithmonë",
|
||||||
|
"freeUsers": "Më e mira për përdorim personal",
|
||||||
|
"freeText01": "100MB depozitë",
|
||||||
|
"freeText02": "3 projekte",
|
||||||
|
"freeText03": "5 anëtarë të ekipit",
|
||||||
|
|
||||||
|
"startupSubtitle": "ÇMIM I RASTËSISHËM / muaj",
|
||||||
|
"startupUsers": "Deri në 15 përdorues",
|
||||||
|
"startupText01": "25GB depozitë",
|
||||||
|
"startupText02": "Projekte të pakufizuara aktive",
|
||||||
|
"startupText03": "Orar",
|
||||||
|
"startupText04": "Raportim",
|
||||||
|
"startupText05": "Abonohu në projekte",
|
||||||
|
|
||||||
|
"businessSubtitle": "përdorues / muaj",
|
||||||
|
"businessUsers": "16 - 200 përdorues",
|
||||||
|
|
||||||
|
"enterpriseUsers": "200 - 500+ përdorues",
|
||||||
|
|
||||||
|
"footerTitle": "Ju lutemi na jepni një numër kontakti që mund të përdorim për t'ju kontaktuar.",
|
||||||
|
"footerLabel": "Numri i Kontaktit",
|
||||||
|
"footerButton": "Na kontaktoni",
|
||||||
|
|
||||||
|
"redeemCodePlaceHolder": "Vendosni kodin tuaj të zbritjes",
|
||||||
|
"submit": "Paraqit",
|
||||||
|
|
||||||
|
"trialPlan": "Provë Falas",
|
||||||
|
"trialExpireDate": "E vlefshme deri më {{trial_expire_date}}",
|
||||||
|
"trialExpired": "Provat tuaja falas skaduan {{trial_expire_string}}",
|
||||||
|
"trialInProgress": "Provat tuaja falas skadojnë {{trial_expire_string}}",
|
||||||
|
|
||||||
|
"required": "Kjo fushë është e detyrueshme",
|
||||||
|
"invalidCode": "Kod i pavlefshëm",
|
||||||
|
|
||||||
|
"selectPlan": "Zgjidhni planin më të mirë për ekipin tuaj",
|
||||||
|
"changeSubscriptionPlan": "Ndryshoni planin tuaj të abonimit",
|
||||||
|
"noOfSeats": "Numri i vendeve",
|
||||||
|
"annualPlan": "Pro - Vjetor",
|
||||||
|
"monthlyPlan": "Pro - Mujor",
|
||||||
|
"freeForever": "Falas Përgjithmonë",
|
||||||
|
"bestForPersonalUse": "Më e mira për përdorim personal",
|
||||||
|
"storage": "Depozitë",
|
||||||
|
"projects": "Projekte",
|
||||||
|
"teamMembers": "Anëtarët e Ekipit",
|
||||||
|
"unlimitedTeamMembers": "Anëtarë të pakufizuar të ekipit",
|
||||||
|
"unlimitedActiveProjects": "Projekte të pakufizuara aktive",
|
||||||
|
"schedule": "Orar",
|
||||||
|
"reporting": "Raportim",
|
||||||
|
"subscribeToProjects": "Abonohu në projekte",
|
||||||
|
"billedAnnually": "Faturuar çdo vit",
|
||||||
|
"billedMonthly": "Faturuar çdo muaj",
|
||||||
|
|
||||||
|
"pausePlan": "Pauzë Planin",
|
||||||
|
"resumePlan": "Rifillo Planin",
|
||||||
|
"changePlan": "Ndrysho Planin",
|
||||||
|
"cancelPlan": "Anulo Planin",
|
||||||
|
|
||||||
|
"perMonthPerUser": "për përdorues/muaj",
|
||||||
|
"viewInvoice": "Shiko Faturën",
|
||||||
|
"switchToFreePlan": "Kalo në Planin Falas",
|
||||||
|
|
||||||
|
"expirestoday": "sot",
|
||||||
|
"expirestomorrow": "nesër",
|
||||||
|
"expiredDaysAgo": "{{days}} ditë më parë",
|
||||||
|
|
||||||
|
"continueWith": "Vazhdo me {{plan}}",
|
||||||
|
"changeToPlan": "Ndrysho në {{plan}}"
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"overview": "Përmbledhje",
|
||||||
|
"name": "Emri i Organizatës",
|
||||||
|
"owner": "Pronari i Organizatës",
|
||||||
|
"admins": "Administruesit e Organizatës",
|
||||||
|
"contactNumber": "Shto Numrin e Kontaktit",
|
||||||
|
"edit": "Redakto"
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"membersCount": "Numri i Anëtarëve",
|
||||||
|
"createdAt": "Krijuar më",
|
||||||
|
"projectName": "Emri i Projektit",
|
||||||
|
"teamName": "Emri i Ekipit",
|
||||||
|
"refreshProjects": "Rifresko Projektet",
|
||||||
|
"searchPlaceholder": "Kërkoni sipas emrit të projektit",
|
||||||
|
"deleteProject": "Jeni i sigurt që dëshironi të fshini këtë projekt?",
|
||||||
|
"confirm": "Konfirmo",
|
||||||
|
"cancel": "Anulo",
|
||||||
|
"delete": "Fshi Projektin"
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"overview": "Përmbledhje",
|
||||||
|
"users": "Përdoruesit",
|
||||||
|
"teams": "Ekipet",
|
||||||
|
"billing": "Faturimi",
|
||||||
|
"projects": "Projektet",
|
||||||
|
"adminCenter": "Qendra Administrative"
|
||||||
|
}
|
||||||
33
worklenz-frontend/public/locales/alb/admin-center/teams.json
Normal file
33
worklenz-frontend/public/locales/alb/admin-center/teams.json
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
{
|
||||||
|
"title": "Ekipet",
|
||||||
|
"subtitle": "ekipet",
|
||||||
|
"tooltip": "Rifresko ekipet",
|
||||||
|
"placeholder": "Kërko sipas emrit",
|
||||||
|
"addTeam": "Shto Ekip",
|
||||||
|
"team": "Ekipi",
|
||||||
|
"membersCount": "Numri i Anëtarëve",
|
||||||
|
"members": "Anëtarët",
|
||||||
|
"drawerTitle": "Krijo Ekip të Ri",
|
||||||
|
"label": "Emri i Ekipit",
|
||||||
|
"drawerPlaceholder": "Emri",
|
||||||
|
"create": "Krijo",
|
||||||
|
"delete": "Fshi",
|
||||||
|
"settings": "Cilësimet",
|
||||||
|
"popTitle": "Jeni i sigurt?",
|
||||||
|
"message": "Ju lutemi shkruani një Emër",
|
||||||
|
"teamSettings": "Cilësimet e Ekipit",
|
||||||
|
"teamName": "Emri i Ekipit",
|
||||||
|
"teamDescription": "Përshkrimi i Ekipit",
|
||||||
|
"teamMembers": "Anëtarët e Ekipit",
|
||||||
|
"teamMembersCount": "Numri i Anëtarëve të Ekipit",
|
||||||
|
"teamMembersPlaceholder": "Kërko sipas emrit",
|
||||||
|
"addMember": "Shto Anëtar",
|
||||||
|
"add": "Shto",
|
||||||
|
"update": "Përditëso",
|
||||||
|
"teamNamePlaceholder": "Emri i ekipit",
|
||||||
|
"user": "Përdoruesi",
|
||||||
|
"role": "Roli",
|
||||||
|
"owner": "Pronari",
|
||||||
|
"admin": "Administruesi",
|
||||||
|
"member": "Anëtari"
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"title": "Përdoruesit",
|
||||||
|
"subTitle": "përdoruesit",
|
||||||
|
"placeholder": "Kërko sipas emrit",
|
||||||
|
"user": "Përdoruesi",
|
||||||
|
"email": "Email",
|
||||||
|
"lastActivity": "Aktiviteti i Fundit",
|
||||||
|
"refresh": "Rifresko përdoruesit"
|
||||||
|
}
|
||||||
23
worklenz-frontend/public/locales/alb/all-project-list.json
Normal file
23
worklenz-frontend/public/locales/alb/all-project-list.json
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"name": "Emri",
|
||||||
|
"client": "Klienti",
|
||||||
|
"category": "Kategoria",
|
||||||
|
"status": "Statusi",
|
||||||
|
"tasksProgress": "Progresi i Detyrave",
|
||||||
|
"updated_at": "Përditësuar Së Fundi",
|
||||||
|
"members": "Anëtarët",
|
||||||
|
"setting": "Cilësimet",
|
||||||
|
"projects": "Projektet",
|
||||||
|
"refreshProjects": "Rifresko projektet",
|
||||||
|
"all": "Të Gjitha",
|
||||||
|
"favorites": "Të Preferuarat",
|
||||||
|
"archived": "Të Arkivuara",
|
||||||
|
"placeholder": "Kërko sipas emrit",
|
||||||
|
"archive": "Arkivo",
|
||||||
|
"unarchive": "Ç'arkivo",
|
||||||
|
"archiveConfirm": "Jeni i sigurt që doni ta arkivoni këtë projekt?",
|
||||||
|
"unarchiveConfirm": "Jeni i sigurt që doni ta çarkivoni këtë projekt?",
|
||||||
|
"clickToFilter": "Klikoni për të filtruar sipas",
|
||||||
|
"noProjects": "Nuk u gjetën projekte",
|
||||||
|
"addToFavourites": "Shto në të preferuarat"
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"loggingOut": "Po dilni...",
|
||||||
|
"authenticating": "Po autentikoheni...",
|
||||||
|
"gettingThingsReady": "Po përgatiten gjërat për ju..."
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"headerDescription": "Rivendosni fjalëkalimin tuaj",
|
||||||
|
"emailLabel": "Email",
|
||||||
|
"emailPlaceholder": "Vendosni email-in tuaj",
|
||||||
|
"emailRequired": "Ju lutemi vendosni Email-in tuaj!",
|
||||||
|
"resetPasswordButton": "Rivendos Fjalëkalimin",
|
||||||
|
"returnToLoginButton": "Kthehu te Hyrja",
|
||||||
|
"passwordResetSuccessMessage": "Një lidhje për rivendosjen e fjalëkalimit është dërguar në email-in tuaj.",
|
||||||
|
"orText": "OSE",
|
||||||
|
"successTitle": "U dërguan udhëzimet për rivendosje!",
|
||||||
|
"successMessage": "Informacioni për rivendosje është dërguar në email-in tuaj. Ju lutemi kontrolloni email-in."
|
||||||
|
}
|
||||||
27
worklenz-frontend/public/locales/alb/auth/login.json
Normal file
27
worklenz-frontend/public/locales/alb/auth/login.json
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"headerDescription": "Hyni në llogarinë tuaj",
|
||||||
|
"emailLabel": "Email",
|
||||||
|
"emailPlaceholder": "Vendosni email-in tuaj",
|
||||||
|
"emailRequired": "Ju lutemi vendosni Email-in tuaj!",
|
||||||
|
"passwordLabel": "Fjalëkalimi",
|
||||||
|
"passwordPlaceholder": "Vendosni fjalëkalimin",
|
||||||
|
"passwordRequired": "Ju lutemi vendosni Fjalëkalimin!",
|
||||||
|
"rememberMe": "Më mbaj mend",
|
||||||
|
"loginButton": "Hyr",
|
||||||
|
"signupButton": "Regjistrohu",
|
||||||
|
"forgotPasswordButton": "Keni harruar fjalëkalimin?",
|
||||||
|
"signInWithGoogleButton": "Hyr me Google",
|
||||||
|
"dontHaveAccountText": "Nuk keni llogari?",
|
||||||
|
"orText": "OSE",
|
||||||
|
"successMessage": "Jeni futur me sukses!",
|
||||||
|
"loginError": "Hyrja dështoi",
|
||||||
|
"googleLoginError": "Hyrja përmes Google dështoi",
|
||||||
|
"validationMessages": {
|
||||||
|
"email": "Ju lutemi vendosni një adresë email të vlefshme",
|
||||||
|
"password": "Fjalëkalimi duhet të jetë së paku 8 karaktere"
|
||||||
|
},
|
||||||
|
"errorMessages": {
|
||||||
|
"loginErrorTitle": "Hyrja dështoi",
|
||||||
|
"loginErrorMessage": "Ju lutemi kontrolloni email-in dhe fjalëkalimin dhe provoni përsëri"
|
||||||
|
}
|
||||||
|
}
|
||||||
29
worklenz-frontend/public/locales/alb/auth/signup.json
Normal file
29
worklenz-frontend/public/locales/alb/auth/signup.json
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
{
|
||||||
|
"headerDescription": "Regjistrohuni për të filluar",
|
||||||
|
"nameLabel": "Emri i Plotë",
|
||||||
|
"namePlaceholder": "Shkruani emrin tuaj të plotë",
|
||||||
|
"nameRequired": "Ju lutemi shkruani emrin tuaj të plotë!",
|
||||||
|
"nameMinCharacterRequired": "Emri duhet të jetë së paku 4 karaktere!",
|
||||||
|
"emailLabel": "Email",
|
||||||
|
"emailPlaceholder": "Shkruani email-in tuaj",
|
||||||
|
"emailRequired": "Ju lutemi shkruani Email-in tuaj!",
|
||||||
|
"passwordLabel": "Fjalëkalimi",
|
||||||
|
"passwordPlaceholder": "Krijoni një fjalëkalim",
|
||||||
|
"passwordRequired": "Ju lutemi krijoni një Fjalëkalim!",
|
||||||
|
"passwordMinCharacterRequired": "Fjalëkalimi duhet të jetë së paku 8 karaktere!",
|
||||||
|
"passwordPatternRequired": "Fjalëkalimi nuk plotëson kërkesat!",
|
||||||
|
"strongPasswordPlaceholder": "Vendosni një fjalëkalim më të fortë",
|
||||||
|
"passwordValidationAltText": "Fjalëkalimi duhet të përmbajë së paku 8 karaktere me shkronja të mëdha dhe të vogla, një numër dhe një simbol.",
|
||||||
|
"signupSuccessMessage": "Jeni regjistruar me sukses!",
|
||||||
|
"privacyPolicyLink": "Politika e Privatësisë",
|
||||||
|
"termsOfUseLink": "Kushtet e Përdorimit",
|
||||||
|
"bySigningUpText": "Duke u regjistruar, ju pranoni",
|
||||||
|
"andText": "dhe",
|
||||||
|
"signupButton": "Regjistrohu",
|
||||||
|
"signInWithGoogleButton": "Hyr me Google",
|
||||||
|
"alreadyHaveAccountText": "Keni tashmë një llogari?",
|
||||||
|
"loginButton": "Hyr",
|
||||||
|
"orText": "OSE",
|
||||||
|
"reCAPTCHAVerificationError": "Gabim në Verifikimin e reCAPTCHA",
|
||||||
|
"reCAPTCHAVerificationErrorMessage": "Nuk mundëm të verifikojmë reCAPTCHA-n tuaj. Ju lutemi provoni përsëri."
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"title": "Verifikoni Email-in për Rivendosje",
|
||||||
|
"description": "Vendosni fjalëkalimin tuaj të ri",
|
||||||
|
"placeholder": "Vendosni fjalëkalimin tuaj të ri",
|
||||||
|
"confirmPasswordPlaceholder": "Konfirmoni fjalëkalimin e ri",
|
||||||
|
"passwordHint": "Të paktën 8 karaktere, me shkronja të mëdha dhe të vogla, një numër dhe një simbol.",
|
||||||
|
"resetPasswordButton": "Rivendos fjalëkalimin",
|
||||||
|
"orText": "Ose",
|
||||||
|
"resendResetEmail": "Dërgo përsëri email-in e rivendosjes",
|
||||||
|
"passwordRequired": "Ju lutemi vendosni fjalëkalimin e ri",
|
||||||
|
"returnToLoginButton": "Kthehu te Hyrja",
|
||||||
|
"confirmPasswordRequired": "Ju lutemi konfirmoni fjalëkalimin e ri",
|
||||||
|
"passwordMismatch": "Fjalëkalimet nuk përputhen"
|
||||||
|
}
|
||||||
9
worklenz-frontend/public/locales/alb/common.json
Normal file
9
worklenz-frontend/public/locales/alb/common.json
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"login-success": "Hyrja u krye me sukses!",
|
||||||
|
"login-failed": "Hyrja dështoi. Ju lutemi kontrolloni kredencialet dhe provoni përsëri.",
|
||||||
|
"signup-success": "Regjistrimi u krye me sukses! Mirë se erdhët.",
|
||||||
|
"signup-failed": "Regjistrimi dështoi. Ju lutemi sigurohuni që të gjitha fushat e nevojshme janë plotësuar dhe provoni përsëri.",
|
||||||
|
"reconnecting": "Jeni shkëputur nga serveri.",
|
||||||
|
"connection-lost": "Lidhja me serverin dështoi. Ju lutemi kontrolloni lidhjen tuaj me internet.",
|
||||||
|
"connection-restored": "U lidhët me serverin me sukses"
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"formTitle": "Krijoni projektin tuaj të parë",
|
||||||
|
"inputLabel": "Në cilin projekt po punoni aktualisht?",
|
||||||
|
"or": "ose",
|
||||||
|
"templateButton": "Importo nga shablloni",
|
||||||
|
"createFromTemplate": "Krijo nga shablloni",
|
||||||
|
"goBack": "Kthehu Mbrapa",
|
||||||
|
"continue": "Vazhdo",
|
||||||
|
"cancel": "Anulo",
|
||||||
|
"create": "Krijo",
|
||||||
|
"templateDrawerTitle": "Zgjidh nga shabllonet",
|
||||||
|
"createProject": "Krijo Projekt"
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"formTitle": "Krijo detyrën tënde të parë.",
|
||||||
|
"inputLabel": "Shkruaj disa detyra që do të kryesh në",
|
||||||
|
"addAnother": "Shto një tjetër",
|
||||||
|
"goBack": "Kthehu mbrapa",
|
||||||
|
"continue": "Vazhdo"
|
||||||
|
}
|
||||||
46
worklenz-frontend/public/locales/alb/home.json
Normal file
46
worklenz-frontend/public/locales/alb/home.json
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
{
|
||||||
|
"todoList": {
|
||||||
|
"title": "Lista e Detyrave",
|
||||||
|
"refreshTasks": "Rifresko detyrat",
|
||||||
|
"addTask": "+ Shto Detyrë",
|
||||||
|
"noTasks": "Asnjë detyrë",
|
||||||
|
"pressEnter": "Shtyp",
|
||||||
|
"toCreate": "për të krijuar.",
|
||||||
|
"markAsDone": "Shëno si të përfunduar"
|
||||||
|
},
|
||||||
|
"projects": {
|
||||||
|
"title": "Projektet",
|
||||||
|
"refreshProjects": "Rifresko projektet",
|
||||||
|
"noRecentProjects": "Aktualisht nuk jeni caktuar në asnjë projekt.",
|
||||||
|
"noFavouriteProjects": "Asnjë projekt i shënuar si i preferuar.",
|
||||||
|
"recent": "Të Fundit",
|
||||||
|
"favourites": "Të Preferuarat"
|
||||||
|
},
|
||||||
|
"tasks": {
|
||||||
|
"assignedToMe": "Më janë caktuar",
|
||||||
|
"assignedByMe": "I kam caktuar",
|
||||||
|
"all": "Të Gjitha",
|
||||||
|
"today": "Sot",
|
||||||
|
"upcoming": "Ardhj",
|
||||||
|
"overdue": "Të vonuara",
|
||||||
|
"noDueDate": "Pa afat",
|
||||||
|
"noTasks": "Asnjë detyrë për të shfaqur.",
|
||||||
|
"addTask": "+ Shto detyrë",
|
||||||
|
"name": "Emri",
|
||||||
|
"project": "Projekti",
|
||||||
|
"status": "Statusi",
|
||||||
|
"dueDate": "Afati",
|
||||||
|
"dueDatePlaceholder": "Cakto Afatin",
|
||||||
|
"tomorrow": "Nesër",
|
||||||
|
"nextWeek": "Javën e Ardhshme",
|
||||||
|
"nextMonth": "Muajin e Ardhshëm",
|
||||||
|
"projectRequired": "Ju lutemi zgjidhni një projekt",
|
||||||
|
"pressTabToSelectDueDateAndProject": "Shtyp Tab për të zgjedhur afatin dhe projektin",
|
||||||
|
"dueOn": "Detyrat me afat më",
|
||||||
|
"taskRequired": "Ju lutemi shtoni një detyrë",
|
||||||
|
"list": "Listë",
|
||||||
|
"calendar": "Kalendar",
|
||||||
|
"tasks": "Detyrat",
|
||||||
|
"refresh": "Rifresko"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"formTitle": "Fto ekipin tënd të punojë me",
|
||||||
|
"inputLabel": "Fto me email",
|
||||||
|
"addAnother": "Shto një tjetër",
|
||||||
|
"goBack": "Kthehu mbrapa",
|
||||||
|
"continue": "Vazhdo",
|
||||||
|
"skipForNow": "Anashkalo tani për tani"
|
||||||
|
}
|
||||||
23
worklenz-frontend/public/locales/alb/kanban-board.json
Normal file
23
worklenz-frontend/public/locales/alb/kanban-board.json
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"rename": "Riemërto",
|
||||||
|
"delete": "Fshi",
|
||||||
|
"addTask": "Shto Detyrë",
|
||||||
|
"addSectionButton": "Shto Seksion",
|
||||||
|
"changeCategory": "Ndrysho kategorinë",
|
||||||
|
|
||||||
|
"deleteTooltip": "Fshi",
|
||||||
|
"deleteConfirmationTitle": "Jeni i sigurt?",
|
||||||
|
"deleteConfirmationOk": "Po",
|
||||||
|
"deleteConfirmationCancel": "Anulo",
|
||||||
|
|
||||||
|
"dueDate": "Data e përfundimit",
|
||||||
|
"cancel": "Anulo",
|
||||||
|
|
||||||
|
"today": "Sot",
|
||||||
|
"tomorrow": "Nesër",
|
||||||
|
"assignToMe": "Cakto mua",
|
||||||
|
"archive": "Arkivo",
|
||||||
|
|
||||||
|
"newTaskNamePlaceholder": "Shkruaj emrin e detyrës",
|
||||||
|
"newSubtaskNamePlaceholder": "Shkruaj emrin e nëndetyrës"
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"title": "Prova juaj e Worklenz ka skaduar!",
|
||||||
|
"subtitle": "Ju lutemi përmirësoni tani.",
|
||||||
|
"button": "Përmirëso tani",
|
||||||
|
"checking": "Po kontrollohet statusi i abonimit..."
|
||||||
|
}
|
||||||
31
worklenz-frontend/public/locales/alb/navbar.json
Normal file
31
worklenz-frontend/public/locales/alb/navbar.json
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
{
|
||||||
|
"logoAlt": "Logoja e Worklenz",
|
||||||
|
"home": "Kryefaqja",
|
||||||
|
"projects": "Projektet",
|
||||||
|
"schedule": "Orari",
|
||||||
|
"reporting": "Raportimi",
|
||||||
|
"clients": "Klientët",
|
||||||
|
"teams": "Ekipet",
|
||||||
|
"labels": "Etiketa",
|
||||||
|
"jobTitles": "Tituj Pune",
|
||||||
|
"upgradePlan": "Përmirëso Abonimin",
|
||||||
|
"upgradePlanTooltip": "Përmirëso abonimin",
|
||||||
|
"invite": "Fto",
|
||||||
|
"inviteTooltip": "Fto anëtarë të ekipit të bashkohen",
|
||||||
|
"switchTeamTooltip": "Ndrysho ekipin",
|
||||||
|
"help": "Ndihmë",
|
||||||
|
"notificationTooltip": "Shiko njoftimet",
|
||||||
|
"profileTooltip": "Shiko profilin",
|
||||||
|
"adminCenter": "Qendra Administrative",
|
||||||
|
"settings": "Cilësimet",
|
||||||
|
"logOut": "Dil",
|
||||||
|
"notificationsDrawer": {
|
||||||
|
"read": "Lexuara e njoftimet ",
|
||||||
|
"unread": "Njoftimet e palexuara",
|
||||||
|
"markAsRead": "Shëno si të lexuara",
|
||||||
|
"readAndJoin": "Lexo & Bashkohu",
|
||||||
|
"accept": "Prano",
|
||||||
|
"acceptAndJoin": "Prano & Bashkohu",
|
||||||
|
"noNotifications": "Asnjë njoftim"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"nameYourOrganization": "Emërtoni organizatën tuaj.",
|
||||||
|
"worklenzAccountTitle": "Zgjidhni një emër për llogarinë tuaj në Worklenz.",
|
||||||
|
"continue": "Vazhdo"
|
||||||
|
}
|
||||||
7
worklenz-frontend/public/locales/alb/phases-drawer.json
Normal file
7
worklenz-frontend/public/locales/alb/phases-drawer.json
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"configurePhases": "Konfiguro Fazat",
|
||||||
|
"phaseLabel": "Etiketa e Fazës",
|
||||||
|
"enterPhaseName": "Vendosni një emër për etiketën e fazës",
|
||||||
|
"addOption": "Shto Opsion",
|
||||||
|
"phaseOptions": "Opsionet e Fazës:"
|
||||||
|
}
|
||||||
42
worklenz-frontend/public/locales/alb/project-drawer.json
Normal file
42
worklenz-frontend/public/locales/alb/project-drawer.json
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
{
|
||||||
|
"createProject": "Krijo Projekt",
|
||||||
|
"editProject": "Modifiko Projektin",
|
||||||
|
"enterCategoryName": "Vendosni emër për kategorinë",
|
||||||
|
"hitEnterToCreate": "Shtyp Enter për të krijuar!",
|
||||||
|
"enterNotes": "Shënime",
|
||||||
|
"youCanManageClientsUnderSettings": "Mund të menaxhoni klientët nën Cilësimet",
|
||||||
|
"addCategory": "Shto kategori projektit",
|
||||||
|
"newCategory": "Kategori e Re",
|
||||||
|
"notes": "Shënime",
|
||||||
|
"startDate": "Data e Fillimit",
|
||||||
|
"endDate": "Data e Përfundimit",
|
||||||
|
"estimateWorkingDays": "Vlerëso ditët e punës",
|
||||||
|
"estimateManDays": "Vlerëso ditët e punëtorëve",
|
||||||
|
"hoursPerDay": "Orë në ditë",
|
||||||
|
"create": "Krijo",
|
||||||
|
"update": "Përditëso",
|
||||||
|
"delete": "Fshi",
|
||||||
|
"typeToSearchClients": "Shkruani për të kërkuar klientë",
|
||||||
|
"projectColor": "Ngjyra e Projektit",
|
||||||
|
"pleaseEnterAName": "Ju lutemi vendosni një emër",
|
||||||
|
"enterProjectName": "Vendosni emrin e projektit",
|
||||||
|
"name": "Emri",
|
||||||
|
"status": "Statusi",
|
||||||
|
"health": "Gjendja",
|
||||||
|
"category": "Kategoria",
|
||||||
|
"projectManager": "Menaxheri i Projektit",
|
||||||
|
"client": "Klienti",
|
||||||
|
"deleteConfirmation": "Jeni i sigurt që doni të fshini?",
|
||||||
|
"deleteConfirmationDescription": "Kjo do të fshijë të gjitha të dhënat e lidhura dhe nuk mund të zhbëhet.",
|
||||||
|
"yes": "Po",
|
||||||
|
"no": "Jo",
|
||||||
|
"createdAt": "Krijuar më",
|
||||||
|
"updatedAt": "Përditësuar më",
|
||||||
|
"by": "nga",
|
||||||
|
"add": "Shto",
|
||||||
|
"asClient": "si klient",
|
||||||
|
"createClient": "Krijo klient",
|
||||||
|
"searchInputPlaceholder": "Kërko sipas emrit ose emailit",
|
||||||
|
"hoursPerDayValidationMessage": "Orët në ditë duhet të jenë një numër midis 1 dhe 24",
|
||||||
|
"noPermission": "Nuk ka leje"
|
||||||
|
}
|
||||||
14
worklenz-frontend/public/locales/alb/project-view-files.json
Normal file
14
worklenz-frontend/public/locales/alb/project-view-files.json
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"nameColumn": "Emri",
|
||||||
|
"attachedTaskColumn": "Detyra e Bashkangjitur",
|
||||||
|
"sizeColumn": "Madhësia",
|
||||||
|
"uploadedByColumn": "Ngarkuar Nga",
|
||||||
|
"uploadedAtColumn": "Ngarkuar Më",
|
||||||
|
"fileIconAlt": "Ikona e skedarit",
|
||||||
|
"titleDescriptionText": "Të gjitha bashkëngjitjet e detyrave në këtë projekt do të shfahen këtu.",
|
||||||
|
"deleteConfirmationTitle": "Jeni i sigurt?",
|
||||||
|
"deleteConfirmationOk": "Po",
|
||||||
|
"deleteConfirmationCancel": "Anulo",
|
||||||
|
"segmentedTooltip": "Së shpejti! Kaloni midis pamjes listë dhe pamjes miniaturash.",
|
||||||
|
"emptyText": "Nuk ka bashkëngjitje në projekt."
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
{
|
||||||
|
"overview": {
|
||||||
|
"title": "Përmbledhje",
|
||||||
|
"statusOverview": "Përmbledhje Statusi",
|
||||||
|
"priorityOverview": "Përmbledhje Prioriteti",
|
||||||
|
"lastUpdatedTasks": "Detyrat e Përditësuara Së Fundi"
|
||||||
|
},
|
||||||
|
"members": {
|
||||||
|
"title": "Anëtarët",
|
||||||
|
"tooltip": "Anëtarët",
|
||||||
|
"tasksByMembers": "Detyrat sipas anëtarëve",
|
||||||
|
"tasksByMembersTooltip": "Detyrat sipas anëtarëve",
|
||||||
|
"name": "Emri",
|
||||||
|
"taskCount": "Numri i Detyrave",
|
||||||
|
"contribution": "Kontributi",
|
||||||
|
"completed": "Të Përfunduara",
|
||||||
|
"incomplete": "Të Papërfunduara",
|
||||||
|
"overdue": "Të Vonuara",
|
||||||
|
"progress": "Progresi"
|
||||||
|
},
|
||||||
|
"tasks": {
|
||||||
|
"overdueTasks": "Detyrat e Vonuara",
|
||||||
|
"overLoggedTasks": "Detyrat me regjistrim të tepërt",
|
||||||
|
"tasksCompletedEarly": "Detyrat e përfunduara para afatit",
|
||||||
|
"tasksCompletedLate": "Detyrat e përfunduara pas afatit",
|
||||||
|
"overLoggedTasksTooltip": "Detyrat me kohë të regjistruar mbi kohën e vlerësuar",
|
||||||
|
"overdueTasksTooltip": "Detyrat që kanë kaluar afatin e tyre"
|
||||||
|
},
|
||||||
|
"common": {
|
||||||
|
"seeAll": "Shiko të gjitha",
|
||||||
|
"totalLoggedHours": "Orët totale të regjistruara",
|
||||||
|
"totalEstimation": "Vlerësimi total",
|
||||||
|
"completedTasks": "Detyrat e përfunduara",
|
||||||
|
"incompleteTasks": "Detyrat e papërfunduara",
|
||||||
|
"overdueTasks": "Detyrat e vonuara",
|
||||||
|
"overdueTasksTooltip": "Detyrat që kanë kaluar afatin e tyre",
|
||||||
|
"totalLoggedHoursTooltip": "Vlerësimi dhe koha e regjistruar për detyrat.",
|
||||||
|
"includeArchivedTasks": "Përfshi Detyrat e Arkivuara",
|
||||||
|
"export": "Eksporto"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"nameColumn": "Emri",
|
||||||
|
"jobTitleColumn": "Titulli i Punës",
|
||||||
|
"emailColumn": "Email",
|
||||||
|
"tasksColumn": "Detyrat",
|
||||||
|
"taskProgressColumn": "Progresi i Detyrave",
|
||||||
|
"accessColumn": "Qasja",
|
||||||
|
"fileIconAlt": "Ikona e skedarit",
|
||||||
|
"deleteConfirmationTitle": "Jeni i sigurt?",
|
||||||
|
"deleteConfirmationOk": "Po",
|
||||||
|
"deleteConfirmationCancel": "Anulo",
|
||||||
|
"refreshButtonTooltip": "Rifresko anëtarët",
|
||||||
|
"deleteButtonTooltip": "Hiq nga projekti",
|
||||||
|
"memberCount": "Anëtar",
|
||||||
|
"membersCountPlural": "Anëtarë",
|
||||||
|
"emptyText": "Nuk ka bashkëngjitje në projekt."
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"inputPlaceholder": "Shto një koment..",
|
||||||
|
"addButton": "Shto",
|
||||||
|
"cancelButton": "Anulo",
|
||||||
|
"deleteButton": "Fshi"
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"importTaskTemplate": "Importo Shabllon Detyrash",
|
||||||
|
"templateName": "Emri i Shabllonit",
|
||||||
|
"templateDescription": "Përshkrimi i Shabllonit",
|
||||||
|
"selectedTasks": "Detyrat e Përzgjedhura",
|
||||||
|
"tasks": "Detyrat",
|
||||||
|
"templates": "Shabllonet",
|
||||||
|
"remove": "Hiq",
|
||||||
|
"cancel": "Anulo",
|
||||||
|
"import": "Importo"
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"title": "Anëtarët e Projektit",
|
||||||
|
"searchLabel": "Shtoni anëtarë duke shkruar emrin ose email-in e tyre",
|
||||||
|
"searchPlaceholder": "Shkruani emrin ose email-in",
|
||||||
|
"inviteAsAMember": "Fto si anëtar",
|
||||||
|
"inviteNewMemberByEmail": "Fto anëtar të ri me email"
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"importTasks": "Importo detyra",
|
||||||
|
"createTask": "Krijo detyrë",
|
||||||
|
"settings": "Cilësimet",
|
||||||
|
"subscribe": "Abonohu",
|
||||||
|
"unsubscribe": "Ç'abonohu",
|
||||||
|
"deleteProject": "Fshi projektin",
|
||||||
|
"startDate": "Data e fillimit",
|
||||||
|
"endDate": "Data e përfundimit",
|
||||||
|
"projectSettings": "Cilësimet e projektit",
|
||||||
|
"projectSummary": "Përmbledhja e projektit",
|
||||||
|
"receiveProjectSummary": "Merrni një përmbledhje të projektit çdo mbrëmje."
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"title": "Ruaj si Shabllon",
|
||||||
|
"templateName": "Emri i Shabllonit",
|
||||||
|
"includes": "Çfarë duhet të përfshihet në shabllon nga projekti?",
|
||||||
|
"includesOptions": {
|
||||||
|
"statuses": "Statuset",
|
||||||
|
"phases": "Fazat",
|
||||||
|
"labels": "Etiketat"
|
||||||
|
},
|
||||||
|
"taskIncludes": "Çfarë duhet të përfshihet në shabllon nga detyrat?",
|
||||||
|
"taskIncludesOptions": {
|
||||||
|
"statuses": "Statuset",
|
||||||
|
"phases": "Fazat",
|
||||||
|
"labels": "Etiketat",
|
||||||
|
"name": "Emri",
|
||||||
|
"priority": "Prioriteti",
|
||||||
|
"status": "Statusi",
|
||||||
|
"phase": "Faza",
|
||||||
|
"label": "Etiketa",
|
||||||
|
"timeEstimate": "Vlerësimi i Kohës",
|
||||||
|
"description": "Përshkrimi",
|
||||||
|
"subTasks": "Nëndetyrat"
|
||||||
|
},
|
||||||
|
"cancel": "Anulo",
|
||||||
|
"save": "Ruaj",
|
||||||
|
"templateNamePlaceholder": "Shkruani emrin e shabllonit"
|
||||||
|
}
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
{
|
||||||
|
"exportButton": "Eksporto",
|
||||||
|
"timeLogsButton": "Regjistrimet e Kohës",
|
||||||
|
"activityLogsButton": "Regjistrimet e Aktivitetit",
|
||||||
|
"tasksButton": "Detyrat",
|
||||||
|
"searchByNameInputPlaceholder": "Kërko sipas emrit",
|
||||||
|
|
||||||
|
"overviewTab": "Përmbledhje",
|
||||||
|
"timeLogsTab": "Regjistrimet e Kohës",
|
||||||
|
"activityLogsTab": "Regjistrimet e Aktivitetit",
|
||||||
|
"tasksTab": "Detyrat",
|
||||||
|
|
||||||
|
"projectsText": "Projektet",
|
||||||
|
"totalTasksText": "Detyrat Gjithsej",
|
||||||
|
"assignedTasksText": "Detyrat e Caktuara",
|
||||||
|
"completedTasksText": "Detyrat e Përfunduara",
|
||||||
|
"ongoingTasksText": "Detyrat në Vazhdim",
|
||||||
|
"overdueTasksText": "Detyrat e Vonuara",
|
||||||
|
"loggedHoursText": "Orët e Regjistruara",
|
||||||
|
|
||||||
|
"tasksText": "Detyrat",
|
||||||
|
"allText": "Të Gjitha",
|
||||||
|
|
||||||
|
"tasksByProjectsText": "Detyrat Sipas Projekteve",
|
||||||
|
"tasksByStatusText": "Detyrat Sipas Statusit",
|
||||||
|
"tasksByPriorityText": "Detyrat Sipas Prioritetit",
|
||||||
|
|
||||||
|
"todoText": "Për Të Bërë",
|
||||||
|
"doingText": "Duke bërë",
|
||||||
|
"doneText": "E Përfunduar",
|
||||||
|
"lowText": "I Ulët",
|
||||||
|
"mediumText": "I Mesëm",
|
||||||
|
"highText": "I Lartë",
|
||||||
|
|
||||||
|
"billableButton": "Fakturueshme",
|
||||||
|
"billableText": "Fakturueshme",
|
||||||
|
"nonBillableText": "Jo Fakturueshme",
|
||||||
|
|
||||||
|
"timeLogsEmptyPlaceholder": "Asnjë regjistrim kohe për të shfaqur",
|
||||||
|
"loggedText": "Regjistruar",
|
||||||
|
"forText": "për",
|
||||||
|
"inText": "në",
|
||||||
|
"updatedText": "Përditësuar",
|
||||||
|
"fromText": "Nga",
|
||||||
|
"toText": "në",
|
||||||
|
"withinText": "brenda",
|
||||||
|
|
||||||
|
"activityLogsEmptyPlaceholder": "Asnjë regjistrim aktiviteti për të shfaqur",
|
||||||
|
|
||||||
|
"filterByText": "Filtro sipas:",
|
||||||
|
"selectProjectPlaceholder": "Zgjidh Projektin",
|
||||||
|
|
||||||
|
"taskColumn": "Detyra",
|
||||||
|
"nameColumn": "Emri",
|
||||||
|
"projectColumn": "Projekti",
|
||||||
|
"statusColumn": "Statusi",
|
||||||
|
"priorityColumn": "Prioriteti",
|
||||||
|
"dueDateColumn": "Afati",
|
||||||
|
"completedDateColumn": "Data e Përfundimit",
|
||||||
|
"estimatedTimeColumn": "Koha e Vlerësuar",
|
||||||
|
"loggedTimeColumn": "Koha e Regjistruar",
|
||||||
|
"overloggedTimeColumn": "Koha e Tepërt",
|
||||||
|
"daysLeftColumn": "Ditë të Mbetura/Vonuar",
|
||||||
|
"startDateColumn": "Data e Fillimit",
|
||||||
|
"endDateColumn": "Data e Përfundimit",
|
||||||
|
"actualTimeColumn": "Koha Aktuale",
|
||||||
|
"projectHealthColumn": "Gjendja e Projektit",
|
||||||
|
"categoryColumn": "Kategoria",
|
||||||
|
"projectManagerColumn": "Menaxheri i Projektit",
|
||||||
|
|
||||||
|
"tasksStatsOverviewDrawerTitle": "Detyrat e ",
|
||||||
|
"projectsStatsOverviewDrawerTitle": "Projektet e ",
|
||||||
|
|
||||||
|
"cancelledText": "Anuluar",
|
||||||
|
"blockedText": "E Bllokuar",
|
||||||
|
"onHoldText": "Në Pritje",
|
||||||
|
"proposedText": "E Propozuar",
|
||||||
|
"inPlanningText": "Në Planifikim",
|
||||||
|
"inProgressText": "Në Progres",
|
||||||
|
"completedText": "E Përfunduar",
|
||||||
|
"continuousText": "E Vazhdueshme",
|
||||||
|
|
||||||
|
"daysLeftText": "ditë të mbetura",
|
||||||
|
"daysOverdueText": "ditë vonuar",
|
||||||
|
|
||||||
|
"notSetText": "Pa Caktuar",
|
||||||
|
"needsAttentionText": "Kërkon Vëmendje",
|
||||||
|
"atRiskText": "Në Rrezik",
|
||||||
|
"goodText": "Në Rregull"
|
||||||
|
}
|
||||||
35
worklenz-frontend/public/locales/alb/reporting-members.json
Normal file
35
worklenz-frontend/public/locales/alb/reporting-members.json
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
{
|
||||||
|
"yesterdayText": "Dje",
|
||||||
|
"lastSevenDaysText": "7 Ditët e Fundit",
|
||||||
|
"lastWeekText": "Javën e Kaluar",
|
||||||
|
"lastThirtyDaysText": "30 Ditët e Fundit",
|
||||||
|
"lastMonthText": "Muajin e Kaluar",
|
||||||
|
"lastThreeMonthsText": "3 Muajt e Fundit",
|
||||||
|
"allTimeText": "Të Gjitha",
|
||||||
|
"customRangeText": "Interval i Përshtatur",
|
||||||
|
"startDateInputPlaceholder": "Data e fillimit",
|
||||||
|
"EndDateInputPlaceholder": "Data e përfundimit",
|
||||||
|
"filterButton": "Filtro",
|
||||||
|
|
||||||
|
"membersTitle": "Anëtarët",
|
||||||
|
"includeArchivedButton": "Përfshij Projektet e Arkivuara",
|
||||||
|
"exportButton": "Eksporto",
|
||||||
|
"excelButton": "Excel",
|
||||||
|
"searchByNameInputPlaceholder": "Kërko sipas emrit",
|
||||||
|
|
||||||
|
"memberColumn": "Anëtari",
|
||||||
|
"tasksProgressColumn": "Progresi i Detyrave",
|
||||||
|
"tasksAssignedColumn": "Detyrat e Caktuara",
|
||||||
|
"completedTasksColumn": "Detyrat e Përfunduara",
|
||||||
|
"overdueTasksColumn": "Detyrat e Vonuara",
|
||||||
|
"ongoingTasksColumn": "Detyrat në Vazhdim",
|
||||||
|
|
||||||
|
"tasksAssignedColumnTooltip": "Detyrat e caktuara në intervalin e zgjedhur",
|
||||||
|
"overdueTasksColumnTooltip": "Detyrat e vonuara deri në fund të intervalit të zgjedhur",
|
||||||
|
"completedTasksColumnTooltip": "Detyrat e përfunduara në intervalin e zgjedhur",
|
||||||
|
"ongoingTasksColumnTooltip": "Detyrat e filluara por jo të përfunduara ende",
|
||||||
|
|
||||||
|
"todoText": "Për Të Bërë",
|
||||||
|
"doingText": "Duke bërë",
|
||||||
|
"doneText": "E Përfunduar"
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
{
|
||||||
|
"exportButton": "Eksporto",
|
||||||
|
"projectsButton": "Projektet",
|
||||||
|
"membersButton": "Anëtarët",
|
||||||
|
"searchByNameInputPlaceholder": "Kërko sipas emrit",
|
||||||
|
|
||||||
|
"overviewTab": "Përmbledhje",
|
||||||
|
"projectsTab": "Projektet",
|
||||||
|
"membersTab": "Anëtarët",
|
||||||
|
|
||||||
|
"projectsByStatusText": "Projektet Sipas Statusit",
|
||||||
|
"projectsByCategoryText": "Projektet Sipas Kategorisë",
|
||||||
|
"projectsByHealthText": "Projektet Sipas Gjendjes",
|
||||||
|
|
||||||
|
"projectsText": "Projektet",
|
||||||
|
"allText": "Të Gjitha",
|
||||||
|
|
||||||
|
"cancelledText": "Anuluar",
|
||||||
|
"blockedText": "E Bllokuar",
|
||||||
|
"onHoldText": "Në Pritje",
|
||||||
|
"proposedText": "E Propozuar",
|
||||||
|
"inPlanningText": "Në Planifikim",
|
||||||
|
"inProgressText": "Në Progres",
|
||||||
|
"completedText": "E Përfunduar",
|
||||||
|
"continuousText": "E Vazhdueshme",
|
||||||
|
|
||||||
|
"notSetText": "Pa Caktuar",
|
||||||
|
"needsAttentionText": "Kërkon Vëmendje",
|
||||||
|
"atRiskText": "Në Rrezik",
|
||||||
|
"goodText": "Në Rregull",
|
||||||
|
|
||||||
|
"nameColumn": "Emri",
|
||||||
|
"emailColumn": "Email",
|
||||||
|
"projectsColumn": "Projektet",
|
||||||
|
"tasksColumn": "Detyrat",
|
||||||
|
"overdueTasksColumn": "Detyrat e Vonuara",
|
||||||
|
"completedTasksColumn": "Detyrat e Përfunduara",
|
||||||
|
"ongoingTasksColumn": "Detyrat në Vazhdim"
|
||||||
|
}
|
||||||
25
worklenz-frontend/public/locales/alb/reporting-overview.json
Normal file
25
worklenz-frontend/public/locales/alb/reporting-overview.json
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"overviewTitle": "Përmbledhje",
|
||||||
|
"includeArchivedButton": "Përfshij Projektet e Arkivuara",
|
||||||
|
|
||||||
|
"teamCount": "Ekip",
|
||||||
|
"teamCountPlural": "Ekipe",
|
||||||
|
"projectCount": "Projekt",
|
||||||
|
"projectCountPlural": "Projekte",
|
||||||
|
"memberCount": "Anëtar",
|
||||||
|
"memberCountPlural": "Anëtarë",
|
||||||
|
"activeProjectCount": "Projekt Aktiv",
|
||||||
|
"activeProjectCountPlural": "Projekte Aktive",
|
||||||
|
"overdueProjectCount": "Projekt i Vonuar",
|
||||||
|
"overdueProjectCountPlural": "Projekte të Vonuara",
|
||||||
|
"unassignedMemberCount": "Anëtar i Pacaktuar",
|
||||||
|
"unassignedMemberCountPlural": "Anëtarë të Pacaktuar",
|
||||||
|
"memberWithOverdueTaskCount": "Anëtar me Detyrë të Vonuar",
|
||||||
|
"memberWithOverdueTaskCountPlural": "Anëtarë me Detyra të Vonuara",
|
||||||
|
|
||||||
|
"teamsText": "Ekipet",
|
||||||
|
|
||||||
|
"nameColumn": "Emri",
|
||||||
|
"projectsColumn": "Projektet",
|
||||||
|
"membersColumn": "Anëtarët"
|
||||||
|
}
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
{
|
||||||
|
"exportButton": "Eksporto",
|
||||||
|
"membersButton": "Anëtarët",
|
||||||
|
"tasksButton": "Detyrat",
|
||||||
|
"searchByNameInputPlaceholder": "Kërko sipas emrit",
|
||||||
|
|
||||||
|
"overviewTab": "Përmbledhje",
|
||||||
|
"membersTab": "Anëtarët",
|
||||||
|
"tasksTab": "Detyrat",
|
||||||
|
|
||||||
|
"completedTasksText": "Detyrat e Përfunduara",
|
||||||
|
"incompleteTasksText": "Detyrat e Papërfunduara",
|
||||||
|
"overdueTasksText": "Detyrat e Vonuara",
|
||||||
|
"allocatedHoursText": "Orët e Alokuara",
|
||||||
|
"loggedHoursText": "Orët e Regjistruara",
|
||||||
|
|
||||||
|
"tasksText": "Detyrat",
|
||||||
|
"allText": "Të Gjitha",
|
||||||
|
|
||||||
|
"tasksByStatusText": "Detyrat Sipas Statusit",
|
||||||
|
"tasksByPriorityText": "Detyrat Sipas Prioritetit",
|
||||||
|
"tasksByDueDateText": "Detyrat Sipas Afatit",
|
||||||
|
|
||||||
|
"todoText": "Për Të Bërë",
|
||||||
|
"doingText": "Duke bërë",
|
||||||
|
"doneText": "E Përfunduar",
|
||||||
|
"lowText": "I Ulët",
|
||||||
|
"mediumText": "I Mesëm",
|
||||||
|
"highText": "I Lartë",
|
||||||
|
"completedText": "E Përfunduar",
|
||||||
|
"upcomingText": "Në Ardhje",
|
||||||
|
"overdueText": "E Vonuar",
|
||||||
|
"noDueDateText": "Pa Afat",
|
||||||
|
|
||||||
|
"nameColumn": "Emri",
|
||||||
|
"tasksCountColumn": "Numri i Detyrave",
|
||||||
|
"completedTasksColumn": "Detyrat e Përfunduara",
|
||||||
|
"incompleteTasksColumn": "Detyrat e Papërfunduara",
|
||||||
|
"overdueTasksColumn": "Detyrat e Vonuara",
|
||||||
|
"contributionColumn": "Kontributi",
|
||||||
|
"progressColumn": "Progresi",
|
||||||
|
"loggedTimeColumn": "Koha e Regjistruar",
|
||||||
|
"taskColumn": "Detyra",
|
||||||
|
"projectColumn": "Projekti",
|
||||||
|
"statusColumn": "Statusi",
|
||||||
|
"priorityColumn": "Prioriteti",
|
||||||
|
"phaseColumn": "Faza",
|
||||||
|
"dueDateColumn": "Afati",
|
||||||
|
"completedDateColumn": "Data e Përfundimit",
|
||||||
|
"estimatedTimeColumn": "Koha e Vlerësuar",
|
||||||
|
"overloggedTimeColumn": "Koha e Tepërt",
|
||||||
|
"completedOnColumn": "Përfunduar Më",
|
||||||
|
"daysOverdueColumn": "Ditë vonim",
|
||||||
|
|
||||||
|
"groupByText": "Grupo Sipas:",
|
||||||
|
"statusText": "Statusi",
|
||||||
|
"priorityText": "Prioriteti",
|
||||||
|
"phaseText": "Faza"
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
{
|
||||||
|
"searchByNamePlaceholder": "Kërko sipas emrit",
|
||||||
|
"searchByCategoryPlaceholder": "Kërko sipas kategorisë",
|
||||||
|
|
||||||
|
"statusText": "Statusi",
|
||||||
|
"healthText": "Gjendja",
|
||||||
|
"categoryText": "Kategoria",
|
||||||
|
"projectManagerText": "Menaxheri i Projektit",
|
||||||
|
"showFieldsText": "Shfaq fushat",
|
||||||
|
|
||||||
|
"cancelledText": "Anuluar",
|
||||||
|
"blockedText": "E bllokuar",
|
||||||
|
"onHoldText": "Në pritje",
|
||||||
|
"proposedText": "E propozuar",
|
||||||
|
"inPlanningText": "Në planifikim",
|
||||||
|
"inProgressText": "Në progres",
|
||||||
|
"completedText": "E përfunduar",
|
||||||
|
"continuousText": "E vazhdueshme",
|
||||||
|
|
||||||
|
"notSetText": "Pa caktuar",
|
||||||
|
"needsAttentionText": "Kërkon vëmendje",
|
||||||
|
"atRiskText": "Në rrezik",
|
||||||
|
"goodText": "Në rregull",
|
||||||
|
|
||||||
|
"nameText": "Projekti",
|
||||||
|
"estimatedVsActualText": "Vlerësuar vs Aktual",
|
||||||
|
"tasksProgressText": "Progresi i detyrave",
|
||||||
|
"lastActivityText": "Aktiviteti i fundit",
|
||||||
|
"datesText": "Datat e Fillimit/Përfundimit",
|
||||||
|
"daysLeftText": "Ditë të mbetura/vonuar",
|
||||||
|
"projectHealthText": "Gjendja e projektit",
|
||||||
|
"projectUpdateText": "Përditësimi i projektit",
|
||||||
|
"clientText": "Klienti",
|
||||||
|
"teamText": "Ekipi"
|
||||||
|
}
|
||||||
52
worklenz-frontend/public/locales/alb/reporting-projects.json
Normal file
52
worklenz-frontend/public/locales/alb/reporting-projects.json
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
{
|
||||||
|
"projectCount": "Projekt",
|
||||||
|
"projectCountPlural": "Projekte",
|
||||||
|
"includeArchivedButton": "Përfshij Projektet e Arkivuara",
|
||||||
|
"exportButton": "Eksporto",
|
||||||
|
"excelButton": "Excel",
|
||||||
|
|
||||||
|
"projectColumn": "Projekti",
|
||||||
|
"estimatedVsActualColumn": "Vlerësuar vs Aktual",
|
||||||
|
"tasksProgressColumn": "Progresi i Detyrave",
|
||||||
|
"lastActivityColumn": "Aktiviteti i Fundit",
|
||||||
|
"statusColumn": "Statusi",
|
||||||
|
"datesColumn": "Data e Fillimit/Përfundimit",
|
||||||
|
"daysLeftColumn": "Ditë të Mbetura/Vonuar",
|
||||||
|
"projectHealthColumn": "Gjendja e Projektit",
|
||||||
|
"categoryColumn": "Kategoria",
|
||||||
|
"projectUpdateColumn": "Përditësimi i Projektit",
|
||||||
|
"clientColumn": "Klienti",
|
||||||
|
"teamColumn": "Ekipi",
|
||||||
|
"projectManagerColumn": "Menaxheri i Projektit",
|
||||||
|
|
||||||
|
"openButton": "Hap",
|
||||||
|
|
||||||
|
"estimatedText": "Vlerësuar",
|
||||||
|
"actualText": "Aktual",
|
||||||
|
|
||||||
|
"todoText": "Për të Bërë",
|
||||||
|
"doingText": "duke bërë",
|
||||||
|
"doneText": "E Përfunduar",
|
||||||
|
|
||||||
|
"cancelledText": "Anuluar",
|
||||||
|
"blockedText": "E Bllokuar",
|
||||||
|
"onHoldText": "Në Pritje",
|
||||||
|
"proposedText": "E Propozuar",
|
||||||
|
"inPlanningText": "Në Planifikim",
|
||||||
|
"inProgressText": "Në Progres",
|
||||||
|
"completedText": "E Përfunduar",
|
||||||
|
"continuousText": "E Vazhdueshme",
|
||||||
|
|
||||||
|
"daysLeftText": "ditë të mbetura",
|
||||||
|
"dayLeftText": "ditë e mbetur",
|
||||||
|
"daysOverdueText": "ditë vonuar",
|
||||||
|
|
||||||
|
"notSetText": "Pa Caktuar",
|
||||||
|
"needsAttentionText": "Kërkon Vëmendje",
|
||||||
|
"atRiskText": "Në Rrezik",
|
||||||
|
"goodText": "Në Rregull",
|
||||||
|
|
||||||
|
"setCategoryText": "Cakto Kategorinë",
|
||||||
|
"searchByNameInputPlaceholder": "Kërko sipas emrit",
|
||||||
|
"todayText": "Sot"
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"overview": "Përmbledhje",
|
||||||
|
"projects": "Projektet",
|
||||||
|
"members": "Anëtarët",
|
||||||
|
"timeReports": "Raportet e Kohës",
|
||||||
|
"estimateVsActual": "Vlerësimi vs Aktual",
|
||||||
|
"currentOrganizationTooltip": "Organizata aktuale"
|
||||||
|
}
|
||||||
39
worklenz-frontend/public/locales/alb/schedule.json
Normal file
39
worklenz-frontend/public/locales/alb/schedule.json
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
{
|
||||||
|
"today": "Sot",
|
||||||
|
"week": "Javë",
|
||||||
|
"month": "Muaj",
|
||||||
|
|
||||||
|
"settings": "Cilësimet",
|
||||||
|
"workingDays": "Ditët e punës",
|
||||||
|
"monday": "E hënë",
|
||||||
|
"tuesday": "E martë",
|
||||||
|
"wednesday": "E mërkurë",
|
||||||
|
"thursday": "E enjte",
|
||||||
|
"friday": "E premte",
|
||||||
|
"saturday": "E shtunë",
|
||||||
|
"sunday": "E diel",
|
||||||
|
"workingHours": "Orët e punës",
|
||||||
|
"hours": "Orë",
|
||||||
|
"saveButton": "Ruaj",
|
||||||
|
|
||||||
|
"totalAllocation": "Alokimi Total",
|
||||||
|
"timeLogged": "Koha e Regjistruar",
|
||||||
|
"remainingTime": "Koha e Mbetur",
|
||||||
|
"total": "Total",
|
||||||
|
"perDay": "Në Ditë",
|
||||||
|
"tasks": "detyra",
|
||||||
|
"startDate": "Data e Fillimit",
|
||||||
|
"endDate": "Data e Përfundimit",
|
||||||
|
|
||||||
|
"hoursPerDay": "Orë Në Ditë",
|
||||||
|
"totalHours": "Orë Totale",
|
||||||
|
"deleteButton": "Fshi",
|
||||||
|
"cancelButton": "Anulo",
|
||||||
|
|
||||||
|
"tabTitle": "Detyra pa Data Fillimi & Përfundimi",
|
||||||
|
|
||||||
|
"allocatedTime": "Koha e alokuar",
|
||||||
|
"totalLogged": "Total i Regjistruar",
|
||||||
|
"loggedBillable": "Regjistruar Fakturueshme",
|
||||||
|
"loggedNonBillable": "Regjistruar Jo Fakturueshme"
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"categoryColumn": "Kategoria",
|
||||||
|
"deleteConfirmationTitle": "Jeni të sigurt?",
|
||||||
|
"deleteConfirmationOk": "Po",
|
||||||
|
"deleteConfirmationCancel": "Anulo",
|
||||||
|
"associatedTaskColumn": "Projektet e Lidhura",
|
||||||
|
"searchPlaceholder": "Kërko sipas emrit",
|
||||||
|
"emptyText": "Kategoritë mund të krijohen gjatë përditësimit ose krijimit të projekteve.",
|
||||||
|
"colorChangeTooltip": "Klikoni për të ndryshuar ngjyrën"
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"title": "Ndrysho Fjalëkalimin",
|
||||||
|
"currentPassword": "Fjalëkalimi Aktual",
|
||||||
|
"newPassword": "Fjalëkalimi i Ri",
|
||||||
|
"confirmPassword": "Konfirmo Fjalëkalimin",
|
||||||
|
"currentPasswordPlaceholder": "Vendosni fjalëkalimin aktual",
|
||||||
|
"newPasswordPlaceholder": "Fjalëkalimi i Ri",
|
||||||
|
"confirmPasswordPlaceholder": "Konfirmo Fjalëkalimin",
|
||||||
|
"currentPasswordRequired": "Ju lutemi vendosni fjalëkalimin aktual!",
|
||||||
|
"newPasswordRequired": "Ju lutemi vendosni fjalëkalimin e ri!",
|
||||||
|
"passwordValidationError": "Fjalëkalimi duhet të përmbajë të paktën 8 karaktere, me një shkronjë të madhe, një numër dhe një simbol.",
|
||||||
|
"passwordMismatch": "Fjalëkalimet nuk përputhen!",
|
||||||
|
"passwordRequirements": "Fjalëkalimi i ri duhet të jetë së paku 8 karaktere, me një shkronjë të madhe, një numër dhe një simbol.",
|
||||||
|
"updateButton": "Përditëso Fjalëkalimin"
|
||||||
|
}
|
||||||
22
worklenz-frontend/public/locales/alb/settings/clients.json
Normal file
22
worklenz-frontend/public/locales/alb/settings/clients.json
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"nameColumn": "Emri",
|
||||||
|
"projectColumn": "Projekti",
|
||||||
|
"noProjectsAvailable": "Nuk ka projekte të disponueshme",
|
||||||
|
"deleteConfirmationTitle": "Jeni i sigurt?",
|
||||||
|
"deleteConfirmationOk": "Po",
|
||||||
|
"deleteConfirmationCancel": "Anulo",
|
||||||
|
"searchPlaceholder": "Kërko sipas emrit",
|
||||||
|
"createClient": "Krijo Klient",
|
||||||
|
"pinTooltip": "Klikoni për ta fiksuar në menynë kryesore",
|
||||||
|
"createClientDrawerTitle": "Krijo Klient",
|
||||||
|
"updateClientDrawerTitle": "Përditëso Klientin",
|
||||||
|
"nameLabel": "Emri",
|
||||||
|
"namePlaceholder": "Emri",
|
||||||
|
"nameRequiredError": "Ju lutemi shkruani një Emër",
|
||||||
|
"createButton": "Krijo",
|
||||||
|
"updateButton": "Përditëso",
|
||||||
|
"createClientSuccessMessage": "Klienti u krijua me sukses!",
|
||||||
|
"createClientErrorMessage": "Krijimi i klientit dështoi!",
|
||||||
|
"updateClientSuccessMessage": "Klienti u përditësua me sukses!",
|
||||||
|
"updateClientErrorMessage": "Përditësimi i klientit dështoi!"
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"nameColumn": "Emri",
|
||||||
|
"deleteConfirmationTitle": "Jeni i sigurt?",
|
||||||
|
"deleteConfirmationOk": "Po",
|
||||||
|
"deleteConfirmationCancel": "Anulo",
|
||||||
|
"searchPlaceholder": "Kërko sipas emrit",
|
||||||
|
"createJobTitleButton": "Krijo Titull Pune",
|
||||||
|
"pinTooltip": "Klikoni për ta fiksuar në menynë kryesore",
|
||||||
|
"createJobTitleDrawerTitle": "Krijo Titull Pune",
|
||||||
|
"updateJobTitleDrawerTitle": "Përditëso Titullin e Punës",
|
||||||
|
"nameLabel": "Emri",
|
||||||
|
"namePlaceholder": "Emri",
|
||||||
|
"nameRequiredError": "Ju lutemi shkruani një Emër",
|
||||||
|
"createButton": "Krijo",
|
||||||
|
"updateButton": "Përditëso",
|
||||||
|
"createJobTitleSuccessMessage": "Titulli i punës u krijua me sukses!",
|
||||||
|
"createJobTitleErrorMessage": "Krijimi i titullit të punës dështoi!",
|
||||||
|
"updateJobTitleSuccessMessage": "Titulli i punës u përditësua me sukses!",
|
||||||
|
"updateJobTitleErrorMessage": "Përditësimi i titullit të punës dështoi!"
|
||||||
|
}
|
||||||
11
worklenz-frontend/public/locales/alb/settings/labels.json
Normal file
11
worklenz-frontend/public/locales/alb/settings/labels.json
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"labelColumn": "Etiketa",
|
||||||
|
"deleteConfirmationTitle": "Jeni i sigurt?",
|
||||||
|
"deleteConfirmationOk": "Po",
|
||||||
|
"deleteConfirmationCancel": "Anulo",
|
||||||
|
"associatedTaskColumn": "Numri i Detyrave të Lidhura",
|
||||||
|
"searchPlaceholder": "Kërko sipas emrit",
|
||||||
|
"emptyText": "Etiketat mund të krijohen gjatë përditësimit ose krijimit të detyrave.",
|
||||||
|
"pinTooltip": "Klikoni për ta fiksuar në menynë kryesore",
|
||||||
|
"colorChangeTooltip": "Klikoni për të ndryshuar ngjyrën"
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"language": "Gjuha",
|
||||||
|
"language_required": "Gjuha është e detyrueshme",
|
||||||
|
"time_zone": "Zona kohore",
|
||||||
|
"time_zone_required": "Zona kohore është e detyrueshme",
|
||||||
|
"save_changes": "Ruaj Ndryshimet"
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"title": "Cilësimet e Njoftimeve",
|
||||||
|
"emailTitle": "Më dërgo njoftime me email",
|
||||||
|
"emailDescription": "Kjo përfshin caktimet e reja të detyrave",
|
||||||
|
"dailyDigestTitle": "Më dërgo një përmbledhje ditore",
|
||||||
|
"dailyDigestDescription": "Çdo mbrëmje, do të merrni një përmbledhje të aktivitetit të fundit në detyra.",
|
||||||
|
"popupTitle": "Shfaq njoftimet në kompjuterin tim kur Worklenz është i hapur",
|
||||||
|
"popupDescription": "Njoftimet e shfaqura mund të çaktivizohen nga shfletuesi juaj. Ndryshoni cilësimet e shfletuesit për t'i lejuar ato.",
|
||||||
|
"unreadItemsTitle": "Shfaq numrin e artikujve të palexuar",
|
||||||
|
"unreadItemsDescription": "Do të shihni numërimin për çdo njoftim."
|
||||||
|
}
|
||||||
13
worklenz-frontend/public/locales/alb/settings/profile.json
Normal file
13
worklenz-frontend/public/locales/alb/settings/profile.json
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"uploadError": "Mund të ngarkoni vetëm skedarë JPG/PNG!",
|
||||||
|
"uploadSizeError": "Imazhi duhet të jetë më i vogël se 2MB!",
|
||||||
|
"upload": "Ngarko",
|
||||||
|
"nameLabel": "Emri",
|
||||||
|
"nameRequiredError": "Emri është i detyrueshëm",
|
||||||
|
"emailLabel": "Email",
|
||||||
|
"emailRequiredError": "Email-i është i detyrueshëm",
|
||||||
|
"saveChanges": "Ruaj Ndryshimet",
|
||||||
|
"profileJoinedText": "U bashkua një muaj më parë",
|
||||||
|
"profileLastUpdatedText": "Përditësuar një muaj më parë",
|
||||||
|
"avatarTooltip": "Klikoni për të ngarkuar një avatar"
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"nameColumn": "Emri",
|
||||||
|
"editToolTip": "Modifiko",
|
||||||
|
"deleteToolTip": "Fshi",
|
||||||
|
"confirmText": "Jeni i sigurt?",
|
||||||
|
"okText": "Po",
|
||||||
|
"cancelText": "Anulo"
|
||||||
|
}
|
||||||
14
worklenz-frontend/public/locales/alb/settings/sidebar.json
Normal file
14
worklenz-frontend/public/locales/alb/settings/sidebar.json
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"profile": "Profili",
|
||||||
|
"notifications": "Njoftimet",
|
||||||
|
"clients": "Klientët",
|
||||||
|
"job-titles": "Tituj Pune",
|
||||||
|
"labels": "Etiketa",
|
||||||
|
"categories": "Kategoritë",
|
||||||
|
"project-templates": "Shabllonet e Projekteve",
|
||||||
|
"task-templates": "Shabllonet e Detyrave",
|
||||||
|
"team-members": "Anëtarët e Ekipit",
|
||||||
|
"teams": "Ekipet",
|
||||||
|
"change-password": "Ndrysho Fjalëkalimin",
|
||||||
|
"language-and-region": "Gjuha dhe Rajoni"
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"nameColumn": "Emri",
|
||||||
|
"createdColumn": "Krijuar",
|
||||||
|
"editToolTip": "Redakto",
|
||||||
|
"deleteToolTip": "Fshi",
|
||||||
|
"confirmText": "Jeni i sigurt?",
|
||||||
|
"okText": "Po",
|
||||||
|
"cancelText": "Anulo"
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
{
|
||||||
|
"nameColumn": "Emri",
|
||||||
|
"projectsColumn": "Projektet",
|
||||||
|
"emailColumn": "Email",
|
||||||
|
"teamAccessColumn": "Qasja në Ekip",
|
||||||
|
"memberCount": "Anëtar",
|
||||||
|
"membersCountPlural": "Anëtarë",
|
||||||
|
"searchPlaceholder": "Kërko anëtarë sipas emrit",
|
||||||
|
"pinTooltip": "Rifresko listën e anëtarëve",
|
||||||
|
"addMemberButton": "Shto Anëtar të Ri",
|
||||||
|
"editTooltip": "Modifiko anëtarin",
|
||||||
|
"deactivateTooltip": "Çaktivizo anëtarin",
|
||||||
|
"activateTooltip": "Aktivizo anëtarin",
|
||||||
|
"deleteTooltip": "Fshi anëtarin",
|
||||||
|
"confirmDeleteTitle": "Jeni i sigurt që doni të fshini këtë anëtar?",
|
||||||
|
"confirmActivateTitle": "Jeni i sigurt që doni të ndryshoni statusin e këtij anëtari?",
|
||||||
|
"okText": "Po, vazhdo",
|
||||||
|
"cancelText": "Jo, anulo",
|
||||||
|
"deactivatedText": "(Aktualisht i çaktivizuar)",
|
||||||
|
"pendingInvitationText": "(Ftesë në pritje)",
|
||||||
|
"addMemberDrawerTitle": "Shto Anëtar të Ri në Ekip",
|
||||||
|
"updateMemberDrawerTitle": "Përditëso Anëtarin e Ekipit",
|
||||||
|
"addMemberEmailHint": "Anëtarët do të shtohen në ekip pavarësisht nga statusi i pranimit të ftesës",
|
||||||
|
"memberEmailLabel": "Email(o)",
|
||||||
|
"memberEmailPlaceholder": "Vendos adresën email të anëtarit të ekipit",
|
||||||
|
"memberEmailRequiredError": "Ju lutemi vendosni një adresë email të vlefshme",
|
||||||
|
"jobTitleLabel": "Titulli i Punës",
|
||||||
|
"jobTitlePlaceholder": "Zgjidh ose kërko titull pune (Opsionale)",
|
||||||
|
"memberAccessLabel": "Niveli i Qasjes",
|
||||||
|
"addToTeamButton": "Shto Anëtar në Ekip",
|
||||||
|
"updateButton": "Ruaj Ndryshimet",
|
||||||
|
"resendInvitationButton": "Dërgo Përsëri Email-in e Ftesës",
|
||||||
|
"invitationSentSuccessMessage": "Ftesa për ekip u dërgua me sukses!",
|
||||||
|
"createMemberSuccessMessage": "Anëtari i ri i ekipit u shtua me sukses!",
|
||||||
|
"createMemberErrorMessage": "Dështoi shtimi i anëtarit të ri. Ju lutemi provoni përsëri.",
|
||||||
|
"updateMemberSuccessMessage": "Anëtari i ekipit u përditësua me sukses!",
|
||||||
|
"updateMemberErrorMessage": "Dështoi përditësimi i anëtarit. Ju lutemi provoni përsëri.",
|
||||||
|
"memberText": "Anëtar",
|
||||||
|
"adminText": "Administrues",
|
||||||
|
"ownerText": "Pronar i Ekipit",
|
||||||
|
"addedText": "Shtuar",
|
||||||
|
"updatedText": "Përditësuar",
|
||||||
|
"noResultFound": "Shkruani një adresë email dhe shtypni Enter..."
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
{
|
||||||
|
"details": {
|
||||||
|
"task-key": "Çelësi i Detyrës",
|
||||||
|
"phase": "Faza",
|
||||||
|
"assignees": "Përgjegjësit",
|
||||||
|
"due-date": "Data e Përfundimit",
|
||||||
|
"time-estimation": "Vlerësimi i Kohës",
|
||||||
|
"priority": "Prioriteti",
|
||||||
|
"labels": "Etiketa",
|
||||||
|
"billable": "Fakturueshme",
|
||||||
|
"notify": "Njofto",
|
||||||
|
"when-done-notify": "Kur të përfundojë, njofto",
|
||||||
|
"start-date": "Data e Fillimit",
|
||||||
|
"end-date": "Data e Përfundimit",
|
||||||
|
"hide-start-date": "Fshih Datën e Fillimit",
|
||||||
|
"show-start-date": "Shfaq Datën e Fillimit",
|
||||||
|
"hours": "Orë",
|
||||||
|
"minutes": "Minuta"
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"title": "Përshkrimi",
|
||||||
|
"placeholder": "Shtoni një përshkrim më të detajuar..."
|
||||||
|
},
|
||||||
|
"subTasks": {
|
||||||
|
"title": "Nën-Detyrat",
|
||||||
|
"add-sub-task": "+ Shto Nën-Detyrë",
|
||||||
|
"refresh-sub-tasks": "Rifresko Nën-Detyrat"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
{
|
||||||
|
"taskHeader": {
|
||||||
|
"taskNamePlaceholder": "Shkruani detyrën tuaj",
|
||||||
|
"deleteTask": "Fshi Detyrën"
|
||||||
|
},
|
||||||
|
"taskInfoTab": {
|
||||||
|
"title": "Info",
|
||||||
|
"details": {
|
||||||
|
"title": "Detajet",
|
||||||
|
"task-key": "Çelësi i Detyrës",
|
||||||
|
"phase": "Faza",
|
||||||
|
"assignees": "Përgjegjësit",
|
||||||
|
"due-date": "Afati i Përfundimit",
|
||||||
|
"time-estimation": "Vlerësimi i Kohës",
|
||||||
|
"priority": "Prioriteti",
|
||||||
|
"labels": "Etiketa",
|
||||||
|
"billable": "Fakturueshme",
|
||||||
|
"notify": "Njofto",
|
||||||
|
"when-done-notify": "Kur të përfundojë, njofto",
|
||||||
|
"start-date": "Data e Fillimit",
|
||||||
|
"end-date": "Data e Përfundimit",
|
||||||
|
"hide-start-date": "Fshih Datën e Fillimit",
|
||||||
|
"show-start-date": "Shfaq Datën e Fillimit",
|
||||||
|
"hours": "Orë",
|
||||||
|
"minutes": "Minuta"
|
||||||
|
},
|
||||||
|
"labels": {
|
||||||
|
"labelInputPlaceholder": "Kërko ose krijo",
|
||||||
|
"labelsSelectorInputTip": "Shtyp Enter për të krijuar"
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"title": "Përshkrimi",
|
||||||
|
"placeholder": "Shtoni një përshkrim më të detajuar..."
|
||||||
|
},
|
||||||
|
"subTasks": {
|
||||||
|
"title": "Nën-Detyrat",
|
||||||
|
"addSubTask": "+ Shto Nën-Detyrë",
|
||||||
|
"addSubTaskInputPlaceholder": "Shkruani detyrën dhe shtypni Enter",
|
||||||
|
"refreshSubTasks": "Rifresko Nën-Detyrat",
|
||||||
|
"edit": "Modifiko",
|
||||||
|
"delete": "Fshi",
|
||||||
|
"confirmDeleteSubTask": "Jeni i sigurt që doni të fshini këtë nën-detyrë?",
|
||||||
|
"deleteSubTask": "Fshi Nën-Detyrën"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"title": "Varësitë",
|
||||||
|
"addDependency": "+ Shto varësi të re",
|
||||||
|
"blockedBy": "I bllokuar nga",
|
||||||
|
"searchTask": "Shkruani për të kërkuar detyra",
|
||||||
|
"noTasksFound": "Asnjë detyrë nuk u gjet",
|
||||||
|
"confirmDeleteDependency": "Jeni i sigurt që doni të fshini?"
|
||||||
|
},
|
||||||
|
"attachments": {
|
||||||
|
"title": "Bashkëngjitjet",
|
||||||
|
"chooseOrDropFileToUpload": "Zgjidhni ose lëshoni skedar për ngarkim",
|
||||||
|
"uploading": "Po ngarkohet..."
|
||||||
|
},
|
||||||
|
"comments": {
|
||||||
|
"title": "Komentet",
|
||||||
|
"addComment": "+ Shto koment të ri",
|
||||||
|
"noComments": "Asnjë koment ende. Bëhu i pari që komenton!",
|
||||||
|
"delete": "Fshi",
|
||||||
|
"confirmDeleteComment": "Jeni i sigurt që doni të fshini këtë koment?"
|
||||||
|
},
|
||||||
|
"searchInputPlaceholder": "Kërko sipas emrit",
|
||||||
|
"pendingInvitation": "Ftesë në Pritje"
|
||||||
|
},
|
||||||
|
"taskTimeLogTab": {
|
||||||
|
"title": "Regjistri i Kohës",
|
||||||
|
"addTimeLog": "Shto regjistrim të ri kohe",
|
||||||
|
"totalLogged": "Koha totale e regjistruar",
|
||||||
|
"exportToExcel": "Eksporto në Excel",
|
||||||
|
"noTimeLogsFound": "Asnjë regjistrim kohe nuk u gjet"
|
||||||
|
},
|
||||||
|
"taskActivityLogTab": {
|
||||||
|
"title": "Regjistri i Aktivitetit"
|
||||||
|
}
|
||||||
|
}
|
||||||
59
worklenz-frontend/public/locales/alb/task-list-filters.json
Normal file
59
worklenz-frontend/public/locales/alb/task-list-filters.json
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
{
|
||||||
|
"searchButton": "Kërko",
|
||||||
|
"resetButton": "Rivendos",
|
||||||
|
"searchInputPlaceholder": "Kërko sipas emrit",
|
||||||
|
|
||||||
|
"sortText": "Rendit",
|
||||||
|
"statusText": "Statusi",
|
||||||
|
"phaseText": "Faza",
|
||||||
|
"memberText": "Anëtarët",
|
||||||
|
"assigneesText": "Përgjegjësit",
|
||||||
|
"priorityText": "Prioriteti",
|
||||||
|
"labelsText": "Etiketa",
|
||||||
|
"membersText": "Anëtarët",
|
||||||
|
"groupByText": "Grupo sipas",
|
||||||
|
"showArchivedText": "Shfaq të arkivuara",
|
||||||
|
"showFieldsText": "Shfaq fushat",
|
||||||
|
"keyText": "Çelësi",
|
||||||
|
"taskText": "Detyra",
|
||||||
|
"descriptionText": "Përshkrimi",
|
||||||
|
"phasesText": "Fazat",
|
||||||
|
"listText": "Listë",
|
||||||
|
"progressText": "Progresi",
|
||||||
|
"timeTrackingText": "Gjurmimi i Kohës",
|
||||||
|
"timetrackingText": "Gjurmimi i Kohës",
|
||||||
|
"estimationText": "Vlerësimi",
|
||||||
|
"startDateText": "Data e Fillimit",
|
||||||
|
"startdateText": "Data e Fillimit",
|
||||||
|
"endDateText": "Data e Përfundimit",
|
||||||
|
"dueDateText": "Afati",
|
||||||
|
"duedateText": "Afati",
|
||||||
|
"completedDateText": "Data e Përfundimit",
|
||||||
|
"completeddateText": "Data e Përfundimit",
|
||||||
|
"createdDateText": "Data e Krijimit",
|
||||||
|
"createddateText": "Data e Krijimit",
|
||||||
|
"lastUpdatedText": "Përditësuar Së Fundi",
|
||||||
|
"lastupdatedText": "Përditësuar Së Fundi",
|
||||||
|
"reporterText": "Raportuesi",
|
||||||
|
"dueTimeText": "Koha e Afatit",
|
||||||
|
"duetimeText": "Koha e Afatit",
|
||||||
|
|
||||||
|
"lowText": "I ulët",
|
||||||
|
"mediumText": "I mesëm",
|
||||||
|
"highText": "I lartë",
|
||||||
|
|
||||||
|
"createStatusButtonTooltip": "Cilësimet e statusit",
|
||||||
|
"configPhaseButtonTooltip": "Cilësimet e fazës",
|
||||||
|
"noLabelsFound": "Nuk u gjetën etiketa",
|
||||||
|
|
||||||
|
"addStatusButton": "Shto Status",
|
||||||
|
"addPhaseButton": "Shto Fazë",
|
||||||
|
|
||||||
|
"createStatus": "Krijo Status",
|
||||||
|
"name": "Emri",
|
||||||
|
"category": "Kategoria",
|
||||||
|
"selectCategory": "Zgjidh një kategori",
|
||||||
|
"pleaseEnterAName": "Ju lutemi vendosni një emër",
|
||||||
|
"pleaseSelectACategory": "Ju lutemi zgjidhni një kategori",
|
||||||
|
"create": "Krijo"
|
||||||
|
}
|
||||||
63
worklenz-frontend/public/locales/alb/task-list-table.json
Normal file
63
worklenz-frontend/public/locales/alb/task-list-table.json
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
{
|
||||||
|
"keyColumn": "Çelësi",
|
||||||
|
"taskColumn": "Detyra",
|
||||||
|
"descriptionColumn": "Përshkrimi",
|
||||||
|
"progressColumn": "Progresi",
|
||||||
|
"membersColumn": "Anëtarët",
|
||||||
|
"assigneesColumn": "Përgjegjësit",
|
||||||
|
"labelsColumn": "Etiketa",
|
||||||
|
"phasesColumn": "Fazat",
|
||||||
|
"phaseColumn": "Faza",
|
||||||
|
"statusColumn": "Statusi",
|
||||||
|
"priorityColumn": "Prioriteti",
|
||||||
|
"timeTrackingColumn": "Gjurmimi i Kohës",
|
||||||
|
"timetrackingColumn": "Gjurmimi i Kohës",
|
||||||
|
"estimationColumn": "Vlerësimi",
|
||||||
|
"startDateColumn": "Data e Fillimit",
|
||||||
|
"startdateColumn": "Data e Fillimit",
|
||||||
|
"dueDateColumn": "Data e Afatit",
|
||||||
|
"duedateColumn": "Data e Afatit",
|
||||||
|
"completedDateColumn": "Data e Përfundimit",
|
||||||
|
"completeddateColumn": "Data e Përfundimit",
|
||||||
|
"createdDateColumn": "Data e Krijimit",
|
||||||
|
"createddateColumn": "Data e Krijimit",
|
||||||
|
"lastUpdatedColumn": "Përditësuar Së Fundi",
|
||||||
|
"lastupdatedColumn": "Përditësuar Së Fundi",
|
||||||
|
"reporterColumn": "Raportuesi",
|
||||||
|
"dueTimeColumn": "Koha e Afatit",
|
||||||
|
"todoSelectorText": "Për të Bërë",
|
||||||
|
"doingSelectorText": "Duke bërë",
|
||||||
|
"doneSelectorText": "E Përfunduar",
|
||||||
|
|
||||||
|
"lowSelectorText": "I ulët",
|
||||||
|
"mediumSelectorText": "I mesëm",
|
||||||
|
"highSelectorText": "I lartë",
|
||||||
|
|
||||||
|
"selectText": "Zgjidh",
|
||||||
|
"labelsSelectorInputTip": "Shtyp Enter për të krijuar!",
|
||||||
|
|
||||||
|
"addTaskText": "+ Shto Detyrë",
|
||||||
|
"addSubTaskText": "+ Shto Nën-Detyrë",
|
||||||
|
"addTaskInputPlaceholder": "Shkruaj detyrën dhe shtyp Enter",
|
||||||
|
|
||||||
|
"openButton": "Hap",
|
||||||
|
"okButton": "Në rregull",
|
||||||
|
|
||||||
|
"noLabelsFound": "Nuk u gjetën etiketa",
|
||||||
|
"searchInputPlaceholder": "Kërko ose krijo",
|
||||||
|
"assigneeSelectorInviteButton": "Fto një anëtar të ri me email",
|
||||||
|
"labelInputPlaceholder": "Kërko ose krijo",
|
||||||
|
|
||||||
|
"pendingInvitation": "Ftesë në Pritje",
|
||||||
|
|
||||||
|
"contextMenu": {
|
||||||
|
"assignToMe": "Cakto mua",
|
||||||
|
"moveTo": "Zhvendos në",
|
||||||
|
"unarchive": "Ç'arkivizo",
|
||||||
|
"archive": "Arkivizo",
|
||||||
|
"convertToSubTask": "Shndërro në Nën-Detyrë",
|
||||||
|
"convertToTask": "Shndërro në Detyrë",
|
||||||
|
"delete": "Fshi",
|
||||||
|
"searchByNameInputPlaceholder": "Kërko sipas emrit"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"createTaskTemplate": "Krijo Shabllon Detyre",
|
||||||
|
"editTaskTemplate": "Modifiko Shabllon Detyre",
|
||||||
|
"cancelText": "Anulo",
|
||||||
|
"saveText": "Ruaj",
|
||||||
|
"templateNameText": "Emri i Shabllonit",
|
||||||
|
"selectedTasks": "Detyrat e Përzgjedhura",
|
||||||
|
"removeTask": "Hiq",
|
||||||
|
"cancelButton": "Anulo",
|
||||||
|
"saveButton": "Ruaj"
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
{
|
||||||
|
"taskSelected": "detyrë e zgjedhur",
|
||||||
|
"tasksSelected": "detyra të zgjedhura",
|
||||||
|
"changeStatus": "Ndrysho Statusin/ Prioritetin/ Fazat",
|
||||||
|
"changeLabel": "Ndrysho Etiketën",
|
||||||
|
"assignToMe": "Cakto mua",
|
||||||
|
"changeAssignees": "Ndrysho Përgjegjësit",
|
||||||
|
"archive": "Arkivo",
|
||||||
|
"unarchive": "Ç'arkivo",
|
||||||
|
"delete": "Fshi",
|
||||||
|
"moreOptions": "Më shumë opsione",
|
||||||
|
"deselectAll": "Zgjidhja të gjitha",
|
||||||
|
"status": "Statusi",
|
||||||
|
"priority": "Prioriteti",
|
||||||
|
"phase": "Faza",
|
||||||
|
"member": "Anëtar",
|
||||||
|
"createTaskTemplate": "Krijo Shabllon Detyre",
|
||||||
|
"apply": "Apliko",
|
||||||
|
"createLabel": "+ Krijo Etiketë",
|
||||||
|
"hitEnterToCreate": "Shtyp Enter për të krijuar",
|
||||||
|
"pendingInvitation": "Ftesë në Pritje",
|
||||||
|
"noMatchingLabels": "Asnjë etiketë që përputhet",
|
||||||
|
"noLabels": "Asnjë etiketë"
|
||||||
|
}
|
||||||
19
worklenz-frontend/public/locales/alb/template-drawer.json
Normal file
19
worklenz-frontend/public/locales/alb/template-drawer.json
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"title": "Modifiko Shabllon Detyre",
|
||||||
|
"cancelText": "Anulo",
|
||||||
|
"saveText": "Ruaj",
|
||||||
|
"templateNameText": "Emri i Shabllonit",
|
||||||
|
"selectedTasks": "Detyrat e Përzgjedhura",
|
||||||
|
"removeTask": "Hiq",
|
||||||
|
"description": "Përshkrimi",
|
||||||
|
"phase": "Faza",
|
||||||
|
"statuses": "Statuset",
|
||||||
|
"priorities": "Prioritetet",
|
||||||
|
"labels": "Etiketa",
|
||||||
|
"tasks": "Detyrat",
|
||||||
|
"noTemplateSelected": "Asnjë shabllon i përzgjedhur",
|
||||||
|
"noDescription": "Pa përshkrim",
|
||||||
|
"worklenzTemplates": "Shabllonet Worklenz",
|
||||||
|
"yourTemplatesLibrary": "Biblioteka Juaj",
|
||||||
|
"searchTemplates": "Kërko Shabllone"
|
||||||
|
}
|
||||||
23
worklenz-frontend/public/locales/alb/templateDrawer.json
Normal file
23
worklenz-frontend/public/locales/alb/templateDrawer.json
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"bugTracking": "Gjurmimi i Gabimeve",
|
||||||
|
"construction": "Ndërtim",
|
||||||
|
"designCreative": "Dizajn & Kreativ",
|
||||||
|
"education": "Arsim",
|
||||||
|
"finance": "Financë",
|
||||||
|
"hrRecruiting": "Burime Njerëzore & Rekrutim",
|
||||||
|
"informationTechnology": "Teknologji Informacioni",
|
||||||
|
"legal": "Juridik",
|
||||||
|
"manufacturing": "Prodhim",
|
||||||
|
"marketing": "Marketing",
|
||||||
|
"nonprofit": "Jo-fitimprurës",
|
||||||
|
"personalUse": "Përdorim Personal",
|
||||||
|
"salesCRM": "Shitje & CRM",
|
||||||
|
"serviceConsulting": "Shërbime & Këshillim",
|
||||||
|
"softwareDevelopment": "Zhvillim Softueri",
|
||||||
|
"description": "Përshkrimi",
|
||||||
|
"phase": "Faza",
|
||||||
|
"statuses": "Statuset",
|
||||||
|
"priorities": "Prioritetet",
|
||||||
|
"labels": "Etiketa",
|
||||||
|
"tasks": "Detyrat"
|
||||||
|
}
|
||||||
44
worklenz-frontend/public/locales/alb/time-report.json
Normal file
44
worklenz-frontend/public/locales/alb/time-report.json
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
{
|
||||||
|
"includeArchivedProjects": "Përfshij Projektet e Arkivuara",
|
||||||
|
"export": "Eksporto",
|
||||||
|
"timeSheet": "Fletë Kohore",
|
||||||
|
|
||||||
|
"searchByName": "Kërko sipas emrit",
|
||||||
|
"selectAll": "Zgjidh të Gjitha",
|
||||||
|
"teams": "Ekipet",
|
||||||
|
|
||||||
|
"searchByProject": "Kërko sipas emrit të projektit",
|
||||||
|
"projects": "Projektet",
|
||||||
|
|
||||||
|
"searchByCategory": "Kërko sipas emrit të kategorisë",
|
||||||
|
"categories": "Kategoritë",
|
||||||
|
|
||||||
|
"billable": "Fakturueshme",
|
||||||
|
"nonBillable": "Jo Fakturueshme",
|
||||||
|
|
||||||
|
"total": "Total",
|
||||||
|
|
||||||
|
"projectsTimeSheet": "Fletë Kohore e Projekteve",
|
||||||
|
|
||||||
|
"loggedTime": "Koha e Regjistruar(orë)",
|
||||||
|
|
||||||
|
"exportToExcel": "Eksporto në Excel",
|
||||||
|
"logged": "regjistruar",
|
||||||
|
"for": "për",
|
||||||
|
|
||||||
|
"membersTimeSheet": "Fletë Kohore e Anëtarëve",
|
||||||
|
"member": "Anëtar",
|
||||||
|
|
||||||
|
"estimatedVsActual": "Vlerësuar vs Aktual",
|
||||||
|
"workingDays": "Ditë Pune",
|
||||||
|
"manDays": "Ditë Njeri",
|
||||||
|
"days": "Ditë",
|
||||||
|
"estimatedDays": "Ditë të Vlerësuara",
|
||||||
|
"actualDays": "Ditë Aktuale",
|
||||||
|
|
||||||
|
"noCategories": "Nuk u gjetën kategori",
|
||||||
|
"noCategory": "Pa Kategori",
|
||||||
|
"noProjects": "Nuk u gjetën projekte",
|
||||||
|
"noTeams": "Nuk u gjetën ekipe",
|
||||||
|
"noData": "Nuk u gjetën të dhëna"
|
||||||
|
}
|
||||||
5
worklenz-frontend/public/locales/alb/unauthorized.json
Normal file
5
worklenz-frontend/public/locales/alb/unauthorized.json
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"title": "E paautorizuar!",
|
||||||
|
"subtitle": "Nuk jeni të autorizuar të hyni në këtë faqe",
|
||||||
|
"button": "Kthehu në Faqen Kryesore"
|
||||||
|
}
|
||||||
4
worklenz-frontend/public/locales/de/404-page.json
Normal file
4
worklenz-frontend/public/locales/de/404-page.json
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"doesNotExistText": "Entschuldigung, die von Ihnen besuchte Seite existiert nicht.",
|
||||||
|
"backHomeButton": "Zurück zur Startseite"
|
||||||
|
}
|
||||||
31
worklenz-frontend/public/locales/de/account-setup.json
Normal file
31
worklenz-frontend/public/locales/de/account-setup.json
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
{
|
||||||
|
"continue": "Weiter",
|
||||||
|
|
||||||
|
"setupYourAccount": "Richten Sie Ihr Worklenz-Konto ein.",
|
||||||
|
"organizationStepTitle": "Organisation benennen",
|
||||||
|
"organizationStepLabel": "Wählen Sie einen Namen für Ihr Worklenz-Konto.",
|
||||||
|
|
||||||
|
"projectStepTitle": "Erstellen Sie Ihr erstes Projekt",
|
||||||
|
"projectStepLabel": "An welchem Projekt arbeiten Sie gerade?",
|
||||||
|
"projectStepPlaceholder": "z.B. Marketingplan",
|
||||||
|
|
||||||
|
"tasksStepTitle": "Erstellen Sie Ihre ersten Aufgaben",
|
||||||
|
"tasksStepLabel": "Geben Sie einige Aufgaben ein, die Sie in",
|
||||||
|
"tasksStepAddAnother": "Weitere hinzufügen",
|
||||||
|
|
||||||
|
"emailPlaceholder": "E-Mail-Adresse",
|
||||||
|
"invalidEmail": "Bitte geben Sie eine gültige E-Mail-Adresse ein",
|
||||||
|
"or": "oder",
|
||||||
|
"templateButton": "Aus Vorlage importieren",
|
||||||
|
"goBack": "Zurück",
|
||||||
|
"cancel": "Abbrechen",
|
||||||
|
"create": "Erstellen",
|
||||||
|
"templateDrawerTitle": "Aus Vorlagen auswählen",
|
||||||
|
"step3InputLabel": "Per E-Mail einladen",
|
||||||
|
"addAnother": "Weitere hinzufügen",
|
||||||
|
"skipForNow": "Jetzt überspringen",
|
||||||
|
"formTitle": "Erstellen Sie Ihre erste Aufgabe.",
|
||||||
|
"step3Title": "Laden Sie Ihr Team zur Zusammenarbeit ein",
|
||||||
|
"maxMembers": " (Sie können bis zu 5 Mitglieder einladen)",
|
||||||
|
"maxTasks": " (Sie können bis zu 5 Aufgaben erstellen)"
|
||||||
|
}
|
||||||
@@ -0,0 +1,113 @@
|
|||||||
|
{
|
||||||
|
"title": "Abrechnungen",
|
||||||
|
"currentBill": "Aktuelle Rechnung",
|
||||||
|
"configuration": "Konfiguration",
|
||||||
|
"currentPlanDetails": "Aktuelle Plan Details",
|
||||||
|
"upgradePlan": "Plan upgraden",
|
||||||
|
"cardBodyText01": "Kostenlose Testversion",
|
||||||
|
"cardBodyText02": "(Ihr Testplan läuft in 1 Monat 19 Tagen ab)",
|
||||||
|
"redeemCode": "Gutscheincode einlösen",
|
||||||
|
"accountStorage": "Kontospeicher",
|
||||||
|
"used": "Verwendet:",
|
||||||
|
"remaining": "Verbleibend:",
|
||||||
|
"charges": "Gebühren",
|
||||||
|
"tooltip": "Gebühren für den aktuellen Abrechnungszeitraum",
|
||||||
|
"description": "Beschreibung",
|
||||||
|
"billingPeriod": "Abrechnungszeitraum",
|
||||||
|
"billStatus": "Rechnungsstatus",
|
||||||
|
"perUserValue": "Pro Benutzer Wert",
|
||||||
|
"users": "Benutzer",
|
||||||
|
|
||||||
|
"amount": "Betrag",
|
||||||
|
"invoices": "Rechnungen",
|
||||||
|
"transactionId": "Transaktions-ID",
|
||||||
|
"transactionDate": "Transaktionsdatum",
|
||||||
|
"paymentMethod": "Zahlungsmethode",
|
||||||
|
"status": "Status",
|
||||||
|
"ltdUsers": "Sie können bis zu {{ltd_users}} Benutzer hinzufügen.",
|
||||||
|
|
||||||
|
"totalSeats": "Gesamte Plätze",
|
||||||
|
"availableSeats": "Verfügbare Plätze",
|
||||||
|
"addMoreSeats": "Weitere Plätze hinzufügen",
|
||||||
|
|
||||||
|
"drawerTitle": "Gutscheincode einlösen",
|
||||||
|
"label": "Gutscheincode",
|
||||||
|
"drawerPlaceholder": "Geben Sie Ihren Gutscheincode ein",
|
||||||
|
"redeemSubmit": "Einreichen",
|
||||||
|
|
||||||
|
"modalTitle": "Wählen Sie den besten Plan für Ihr Team",
|
||||||
|
"seatLabel": "Anzahl der Plätze",
|
||||||
|
"freePlan": "Kostenloser Plan",
|
||||||
|
"startup": "Startup",
|
||||||
|
"business": "Business",
|
||||||
|
"tag": "Am beliebtesten",
|
||||||
|
"enterprise": "Enterprise",
|
||||||
|
|
||||||
|
"freeSubtitle": "kostenlos für immer",
|
||||||
|
"freeUsers": "Ideal für die persönliche Nutzung",
|
||||||
|
"freeText01": "100MB Speicher",
|
||||||
|
"freeText02": "3 Projekte",
|
||||||
|
"freeText03": "5 Teammitglieder",
|
||||||
|
|
||||||
|
"startupSubtitle": "PAUSCHALPREIS / Monat",
|
||||||
|
"startupUsers": "Bis zu 15 Benutzer",
|
||||||
|
"startupText01": "25GB Speicher",
|
||||||
|
"startupText02": "Unbegrenzte aktive Projekte",
|
||||||
|
"startupText03": "Zeitplan",
|
||||||
|
"startupText04": "Berichterstattung",
|
||||||
|
"startupText05": "Projekte abonnieren",
|
||||||
|
|
||||||
|
"businessSubtitle": "Benutzer / Monat",
|
||||||
|
"businessUsers": "16 - 200 Benutzer",
|
||||||
|
|
||||||
|
"enterpriseUsers": "200 - 500+ Benutzer",
|
||||||
|
|
||||||
|
"footerTitle": "Bitte geben Sie uns eine Kontaktnummer, unter der wir Sie erreichen können.",
|
||||||
|
"footerLabel": "Kontaktnummer",
|
||||||
|
"footerButton": "Kontaktieren Sie uns",
|
||||||
|
|
||||||
|
"redeemCodePlaceHolder": "Geben Sie Ihren Gutscheincode ein",
|
||||||
|
"submit": "Einreichen",
|
||||||
|
|
||||||
|
"trialPlan": "Kostenlose Testversion",
|
||||||
|
"trialExpireDate": "Gültig bis {{trial_expire_date}}",
|
||||||
|
"trialExpired": "Ihre kostenlose Testversion ist {{trial_expire_string}} abgelaufen",
|
||||||
|
"trialInProgress": "Ihre kostenlose Testversion läuft {{trial_expire_string}} ab",
|
||||||
|
|
||||||
|
"required": "Dieses Feld ist erforderlich",
|
||||||
|
"invalidCode": "Ungültiger Code",
|
||||||
|
|
||||||
|
"selectPlan": "Wählen Sie den besten Plan für Ihr Team",
|
||||||
|
"changeSubscriptionPlan": "Ändern Sie Ihren Abonnementplan",
|
||||||
|
"noOfSeats": "Anzahl der Plätze",
|
||||||
|
"annualPlan": "Pro - Jährlich",
|
||||||
|
"monthlyPlan": "Pro - Monatlich",
|
||||||
|
"freeForever": "Kostenlos für immer",
|
||||||
|
"bestForPersonalUse": "Ideal für die persönliche Nutzung",
|
||||||
|
"storage": "Speicher",
|
||||||
|
"projects": "Projekte",
|
||||||
|
"teamMembers": "Teammitglieder",
|
||||||
|
"unlimitedTeamMembers": "Unbegrenzte Teammitglieder",
|
||||||
|
"unlimitedActiveProjects": "Unbegrenzte aktive Projekte",
|
||||||
|
"schedule": "Zeitplan",
|
||||||
|
"reporting": "Berichterstattung",
|
||||||
|
"subscribeToProjects": "Projekte abonnieren",
|
||||||
|
"billedAnnually": "Jährlich abgerechnet",
|
||||||
|
"billedMonthly": "Monatlich abgerechnet",
|
||||||
|
|
||||||
|
"pausePlan": "Plan pausieren",
|
||||||
|
"resumePlan": "Plan fortsetzen",
|
||||||
|
"changePlan": "Plan ändern",
|
||||||
|
"cancelPlan": "Plan kündigen",
|
||||||
|
|
||||||
|
"perMonthPerUser": "pro Benutzer/Monat",
|
||||||
|
"viewInvoice": "Rechnung anzeigen",
|
||||||
|
"switchToFreePlan": "Wechsel zum kostenlosen Plan",
|
||||||
|
|
||||||
|
"expirestoday": "heute",
|
||||||
|
"expirestomorrow": "morgen",
|
||||||
|
"expiredDaysAgo": "vor {{days}} Tagen",
|
||||||
|
|
||||||
|
"continueWith": "Fortfahren mit {{plan}}",
|
||||||
|
"changeToPlan": "Wechseln zu {{plan}}"
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"overview": "Übersicht",
|
||||||
|
"name": "Organisationsname",
|
||||||
|
"owner": "Organisationsinhaber",
|
||||||
|
"admins": "Organisationsadministratoren",
|
||||||
|
"contactNumber": "Kontaktnummer hinzufügen",
|
||||||
|
"edit": "Bearbeiten"
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"membersCount": "Mitgliederanzahl",
|
||||||
|
"createdAt": "Erstellt am",
|
||||||
|
"projectName": "Projektname",
|
||||||
|
"teamName": "Teamname",
|
||||||
|
"refreshProjects": "Projekte aktualisieren",
|
||||||
|
"searchPlaceholder": "Nach Projektname suchen",
|
||||||
|
"deleteProject": "Sind Sie sicher, dass Sie dieses Projekt löschen möchten?",
|
||||||
|
"confirm": "Bestätigen",
|
||||||
|
"cancel": "Abbrechen",
|
||||||
|
"delete": "Projekt löschen"
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"overview": "Übersicht",
|
||||||
|
"users": "Benutzer",
|
||||||
|
"teams": "Teams",
|
||||||
|
"billing": "Abrechnung",
|
||||||
|
"projects": "Projekte",
|
||||||
|
"adminCenter": "Admin-Center"
|
||||||
|
}
|
||||||
33
worklenz-frontend/public/locales/de/admin-center/teams.json
Normal file
33
worklenz-frontend/public/locales/de/admin-center/teams.json
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
{
|
||||||
|
"title": "Teams",
|
||||||
|
"subtitle": "Teams",
|
||||||
|
"tooltip": "Teams aktualisieren",
|
||||||
|
"placeholder": "Nach Namen suchen",
|
||||||
|
"addTeam": "Team hinzufügen",
|
||||||
|
"team": "Team",
|
||||||
|
"membersCount": "Mitgliederanzahl",
|
||||||
|
"members": "Mitglieder",
|
||||||
|
"drawerTitle": "Neues Team erstellen",
|
||||||
|
"label": "Teamname",
|
||||||
|
"drawerPlaceholder": "Name",
|
||||||
|
"create": "Erstellen",
|
||||||
|
"delete": "Löschen",
|
||||||
|
"settings": "Einstellungen",
|
||||||
|
"popTitle": "Sind Sie sicher?",
|
||||||
|
"message": "Bitte geben Sie einen Namen ein",
|
||||||
|
"teamSettings": "Team-Einstellungen",
|
||||||
|
"teamName": "Teamname",
|
||||||
|
"teamDescription": "Teambeschreibung",
|
||||||
|
"teamMembers": "Teammitglieder",
|
||||||
|
"teamMembersCount": "Anzahl der Teammitglieder",
|
||||||
|
"teamMembersPlaceholder": "Nach Namen suchen",
|
||||||
|
"addMember": "Mitglied hinzufügen",
|
||||||
|
"add": "Hinzufügen",
|
||||||
|
"update": "Aktualisieren",
|
||||||
|
"teamNamePlaceholder": "Name des Teams",
|
||||||
|
"user": "Benutzer",
|
||||||
|
"role": "Rolle",
|
||||||
|
"owner": "Besitzer",
|
||||||
|
"admin": "Administrator",
|
||||||
|
"member": "Mitglied"
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"title": "Benutzer",
|
||||||
|
"subTitle": "Benutzer",
|
||||||
|
"placeholder": "Nach Namen suchen",
|
||||||
|
"user": "Benutzer",
|
||||||
|
"email": "E-Mail",
|
||||||
|
"lastActivity": "Letzte Aktivität",
|
||||||
|
"refresh": "Benutzer aktualisieren"
|
||||||
|
}
|
||||||
23
worklenz-frontend/public/locales/de/all-project-list.json
Normal file
23
worklenz-frontend/public/locales/de/all-project-list.json
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"name": "Name",
|
||||||
|
"client": "Kunde",
|
||||||
|
"category": "Kategorie",
|
||||||
|
"status": "Status",
|
||||||
|
"tasksProgress": "Aufgabenfortschritt",
|
||||||
|
"updated_at": "Zuletzt aktualisiert",
|
||||||
|
"members": "Mitglieder",
|
||||||
|
"setting": "Einstellungen",
|
||||||
|
"projects": "Projekte",
|
||||||
|
"refreshProjects": "Projekte aktualisieren",
|
||||||
|
"all": "Alle",
|
||||||
|
"favorites": "Favoriten",
|
||||||
|
"archived": "Archiviert",
|
||||||
|
"placeholder": "Nach Namen suchen",
|
||||||
|
"archive": "Archivieren",
|
||||||
|
"unarchive": "Dearchivieren",
|
||||||
|
"archiveConfirm": "Sind Sie sicher, dass Sie dieses Projekt archivieren möchten?",
|
||||||
|
"unarchiveConfirm": "Sind Sie sicher, dass Sie dieses Projekt dearchivieren möchten?",
|
||||||
|
"clickToFilter": "Zum Filtern klicken nach",
|
||||||
|
"noProjects": "Keine Projekte gefunden",
|
||||||
|
"addToFavourites": "Zu Favoriten hinzufügen"
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"loggingOut": "Abmelden...",
|
||||||
|
"authenticating": "Authentifizierung läuft...",
|
||||||
|
"gettingThingsReady": "Bereite alles für Sie vor..."
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"headerDescription": "Passwort zurücksetzen",
|
||||||
|
"emailLabel": "E-Mail",
|
||||||
|
"emailPlaceholder": "Ihre E-Mail eingeben",
|
||||||
|
"emailRequired": "Bitte geben Sie Ihre E-Mail-Adresse ein!",
|
||||||
|
"resetPasswordButton": "Passwort zurücksetzen",
|
||||||
|
"returnToLoginButton": "Zurück zum Login",
|
||||||
|
"passwordResetSuccessMessage": "Ein Link zum Zurücksetzen des Passworts wurde an Ihre E-Mail gesendet.",
|
||||||
|
"orText": "ODER",
|
||||||
|
"successTitle": "Anweisung zum Zurücksetzen gesendet!",
|
||||||
|
"successMessage": "Die Informationen zum Zurücksetzen wurden an Ihre E-Mail gesendet. Bitte überprüfen Sie Ihr E-Mail-Postfach."
|
||||||
|
}
|
||||||
27
worklenz-frontend/public/locales/de/auth/login.json
Normal file
27
worklenz-frontend/public/locales/de/auth/login.json
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"headerDescription": "Melden Sie sich an",
|
||||||
|
"emailLabel": "E-Mail",
|
||||||
|
"emailPlaceholder": "Ihre E-Mail-Adresse eingeben",
|
||||||
|
"emailRequired": "Bitte geben Sie Ihre E-Mail-Adresse ein!",
|
||||||
|
"passwordLabel": "Passwort",
|
||||||
|
"passwordPlaceholder": "Ihr Passwort eingeben",
|
||||||
|
"passwordRequired": "Bitte geben Sie Ihr Passwort ein!",
|
||||||
|
"rememberMe": "Erinnere dich an mich",
|
||||||
|
"loginButton": "Anmelden",
|
||||||
|
"signupButton": "Registrieren",
|
||||||
|
"forgotPasswordButton": "Passwort vergessen?",
|
||||||
|
"signInWithGoogleButton": "Mit Google anmelden",
|
||||||
|
"dontHaveAccountText": "Noch kein Konto?",
|
||||||
|
"orText": "ODER",
|
||||||
|
"successMessage": "Sie haben sich erfolgreich angemeldet!",
|
||||||
|
"loginError": "Anmeldung fehlgeschlagen",
|
||||||
|
"googleLoginError": "Google-Anmeldung fehlgeschlagen",
|
||||||
|
"validationMessages": {
|
||||||
|
"email": "Bitte geben Sie eine gültige E-Mail-Adresse ein",
|
||||||
|
"password": "Das Passwort muss mindestens 8 Zeichen lang sein"
|
||||||
|
},
|
||||||
|
"errorMessages": {
|
||||||
|
"loginErrorTitle": "Anmeldung fehlgeschlagen",
|
||||||
|
"loginErrorMessage": "Bitte überprüfen Sie Ihre E-Mail-Adresse und Ihr Passwort und versuchen Sie es erneut"
|
||||||
|
}
|
||||||
|
}
|
||||||
29
worklenz-frontend/public/locales/de/auth/signup.json
Normal file
29
worklenz-frontend/public/locales/de/auth/signup.json
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
{
|
||||||
|
"headerDescription": "Registrieren Sie sich, um loszulegen",
|
||||||
|
"nameLabel": "Vollständiger Name",
|
||||||
|
"namePlaceholder": "Ihren vollständigen Namen eingeben",
|
||||||
|
"nameRequired": "Bitte geben Sie Ihren vollständigen Namen ein!",
|
||||||
|
"nameMinCharacterRequired": "Der Name muss mindestens 4 Zeichen lang sein!",
|
||||||
|
"emailLabel": "E-Mail",
|
||||||
|
"emailPlaceholder": "Ihre E-Mail-Adresse eingeben",
|
||||||
|
"emailRequired": "Bitte geben Sie Ihre E-Mail-Adresse ein!",
|
||||||
|
"passwordLabel": "Passwort",
|
||||||
|
"passwordPlaceholder": "Ihr Passwort eingeben",
|
||||||
|
"passwordRequired": "Bitte geben Sie Ihr Passwort ein!",
|
||||||
|
"passwordMinCharacterRequired": "Das Passwort muss mindestens 8 Zeichen lang sein!",
|
||||||
|
"passwordPatternRequired": "Das Passwort erfüllt nicht die Anforderungen!",
|
||||||
|
"strongPasswordPlaceholder": "Ein stärkeres Passwort eingeben",
|
||||||
|
"passwordValidationAltText": "Das Passwort muss mindestens 8 Zeichen enthalten, mit Groß- und Kleinbuchstaben, einer Zahl und einem Sonderzeichen.",
|
||||||
|
"signupSuccessMessage": "Sie haben sich erfolgreich registriert!",
|
||||||
|
"privacyPolicyLink": "Datenschutzrichtlinie",
|
||||||
|
"termsOfUseLink": "Nutzungsbedingungen",
|
||||||
|
"bySigningUpText": "Mit der Registrierung stimmen Sie unseren",
|
||||||
|
"andText": "und",
|
||||||
|
"signupButton": "Registrieren",
|
||||||
|
"signInWithGoogleButton": "Mit Google anmelden",
|
||||||
|
"alreadyHaveAccountText": "Sie haben bereits ein Konto?",
|
||||||
|
"loginButton": "Anmelden",
|
||||||
|
"orText": "ODER",
|
||||||
|
"reCAPTCHAVerificationError": "reCAPTCHA-Verifizierungsfehler",
|
||||||
|
"reCAPTCHAVerificationErrorMessage": "Wir konnten Ihre reCAPTCHA nicht verifizieren. Bitte versuchen Sie es erneut."
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"title": "E-Mail zurücksetzen bestätigen",
|
||||||
|
"description": "Geben Sie Ihr neues Passwort ein",
|
||||||
|
"placeholder": "Neues Passwort eingeben",
|
||||||
|
"confirmPasswordPlaceholder": "Neues Passwort bestätigen",
|
||||||
|
"passwordHint": "Mindestens 8 Zeichen, mit Groß- und Kleinbuchstaben, einer Zahl und einem Sonderzeichen.",
|
||||||
|
"resetPasswordButton": "Passwort zurücksetzen",
|
||||||
|
"orText": "Oder",
|
||||||
|
"resendResetEmail": "Zurücksetz-E-Mail erneut senden",
|
||||||
|
"passwordRequired": "Bitte geben Sie Ihr neues Passwort ein",
|
||||||
|
"returnToLoginButton": "Zurück zur Anmeldung",
|
||||||
|
"confirmPasswordRequired": "Bitte bestätigen Sie Ihr neues Passwort",
|
||||||
|
"passwordMismatch": "Die beiden Passwörter stimmen nicht überein"
|
||||||
|
}
|
||||||
9
worklenz-frontend/public/locales/de/common.json
Normal file
9
worklenz-frontend/public/locales/de/common.json
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"login-success": "Anmeldung erfolgreich!",
|
||||||
|
"login-failed": "Anmeldung fehlgeschlagen. Bitte überprüfen Sie Ihre Anmeldedaten und versuchen Sie es erneut.",
|
||||||
|
"signup-success": "Registrierung erfolgreich! Willkommen an Bord.",
|
||||||
|
"signup-failed": "Registrierung fehlgeschlagen. Bitte füllen Sie alle erforderlichen Felder aus und versuchen Sie es erneut.",
|
||||||
|
"reconnecting": "Vom Server getrennt.",
|
||||||
|
"connection-lost": "Verbindung zum Server fehlgeschlagen. Bitte überprüfen Sie Ihre Internetverbindung.",
|
||||||
|
"connection-restored": "Erfolgreich mit dem Server verbunden"
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"formTitle": "Erstellen Sie Ihr erstes Projekt",
|
||||||
|
"inputLabel": "An welchem Projekt arbeiten Sie gerade?",
|
||||||
|
"or": "oder",
|
||||||
|
"templateButton": "Aus Vorlage importieren",
|
||||||
|
"createFromTemplate": "Aus Vorlage erstellen",
|
||||||
|
"goBack": "Zurück",
|
||||||
|
"continue": "Weitermachen",
|
||||||
|
"cancel": "Abbrechen",
|
||||||
|
"create": "Erstellen",
|
||||||
|
"templateDrawerTitle": "Aus Vorlagen auswählen",
|
||||||
|
"createProject": "Projekt erstellen"
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"formTitle": "Erstellen Sie Ihre erste Aufgabe.",
|
||||||
|
"inputLabel": "Geben Sie einige Aufgaben ein, die Sie erledigen werden in",
|
||||||
|
"addAnother": "Einen weiteren hinzufügen",
|
||||||
|
"goBack": "Zurück",
|
||||||
|
"continue": "Weiter"
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user