fix: multiple improvements and CI/CD updates
Frontend fixes: - Add missing translations for chunk upload (upload.uploadingChunks, common.chunk) - Fix photo deletion visual bug by tracking deletion state per photo - Prevent UI confusion when deleting photos in admin grid Backend fixes: - Add file existence checks before deleting thumbnails - Prevent ENOENT errors for missing thumbnail files - Improve error handling in photo deletion CI/CD updates: - Remove Gitea release creation from Drone pipeline - Simplify GitHub mirror workflow (remove history rewriting, keep file removal) - Add clean-git-history.sh script for manual history cleanup These changes improve the admin photo management experience and streamline the CI/CD process for better maintainability. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
-46
@@ -108,52 +108,6 @@ steps:
|
||||
- VERSION=${DRONE_TAG}
|
||||
- VITE_API_URL=${VITE_API_URL:-/api}
|
||||
|
||||
# -------- NEW: Create Gitea Release --------
|
||||
- name: gitea-release
|
||||
image: plugins/gitea-release
|
||||
when:
|
||||
event: tag
|
||||
settings:
|
||||
api_key:
|
||||
from_secret: GITEA_TOKEN
|
||||
base_url: https://gitea.local.nothaft.cloud
|
||||
files: []
|
||||
title: PicPeak ${DRONE_TAG}
|
||||
note: |
|
||||
# PicPeak ${DRONE_TAG}
|
||||
|
||||
## 🐳 Docker Images
|
||||
|
||||
This release includes Docker images published to local registry:
|
||||
|
||||
```bash
|
||||
# Backend
|
||||
docker pull registry.local.nothaft.cloud/picpeak-backend:${DRONE_TAG}
|
||||
docker pull registry.local.nothaft.cloud/picpeak-backend:latest
|
||||
|
||||
# Frontend
|
||||
docker pull registry.local.nothaft.cloud/picpeak-frontend:${DRONE_TAG}
|
||||
docker pull registry.local.nothaft.cloud/picpeak-frontend:latest
|
||||
```
|
||||
|
||||
## 📦 What's New
|
||||
|
||||
See the [README](https://gitea.nothaft.cloud/paul/picpeak#readme) for features and documentation.
|
||||
|
||||
## 🚀 Quick Start
|
||||
|
||||
```bash
|
||||
# Clone and deploy
|
||||
git clone https://gitea.nothaft.cloud/paul/picpeak.git
|
||||
cd picpeak
|
||||
|
||||
# Use the tagged version
|
||||
docker-compose -f docker-compose.prod.yml up -d
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
For detailed deployment instructions, see the Deployment Guide in the repository.
|
||||
|
||||
trigger:
|
||||
event:
|
||||
|
||||
@@ -10,120 +10,18 @@ jobs:
|
||||
mirror:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository with full history
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0 # Full history needed for finding the commit
|
||||
fetch-depth: 0 # Full history for proper mirroring
|
||||
|
||||
- name: Setup Git
|
||||
run: |
|
||||
git config --global user.name "the-luap"
|
||||
git config --global user.email "paul-nothaft@hotmail.de"
|
||||
|
||||
- name: Debug - Show current branch and status
|
||||
run: |
|
||||
echo "Current branch:"
|
||||
git branch -a
|
||||
echo "Git status:"
|
||||
git status
|
||||
echo "Remote info:"
|
||||
git remote -v
|
||||
echo "Checking target commit exists:"
|
||||
git show --oneline 7aca927937 || echo "Target commit not found!"
|
||||
|
||||
- name: Create completely new history from specific commit
|
||||
run: |
|
||||
TARGET_COMMIT="7aca927937"
|
||||
|
||||
# Verify the target commit exists
|
||||
if ! git cat-file -e $TARGET_COMMIT^{commit}; then
|
||||
echo "ERROR: Target commit $TARGET_COMMIT does not exist!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "✅ Target commit found: $(git log --oneline -1 $TARGET_COMMIT)"
|
||||
|
||||
# Clean up any existing github-mirror branch
|
||||
git branch -D github-mirror || true
|
||||
|
||||
# Create a completely new orphan branch (no history)
|
||||
git checkout --orphan github-mirror
|
||||
|
||||
# Clear the staging area completely
|
||||
git rm -rf . || true
|
||||
|
||||
# Get the file tree from the target commit and create initial commit
|
||||
echo "Creating new history starting from $TARGET_COMMIT..."
|
||||
git read-tree $TARGET_COMMIT
|
||||
git commit -m "Initial commit - imported from $(git log --oneline -1 $TARGET_COMMIT)"
|
||||
|
||||
echo "✅ Created new initial commit: $(git log --oneline -1)"
|
||||
|
||||
# Now get all commits after the target commit and apply their changes
|
||||
COMMITS_AFTER_TARGET=$(git rev-list --reverse --no-merges $TARGET_COMMIT..main)
|
||||
|
||||
if [ -n "$COMMITS_AFTER_TARGET" ]; then
|
||||
echo "📋 Applying changes from commits after $TARGET_COMMIT (excluding Claude commits):"
|
||||
|
||||
for commit in $COMMITS_AFTER_TARGET; do
|
||||
# Get the commit author name
|
||||
COMMIT_AUTHOR_NAME=$(git log --format="%an" -n 1 $commit)
|
||||
|
||||
# Skip commits by Claude
|
||||
if [ "$COMMIT_AUTHOR_NAME" = "Claude" ]; then
|
||||
echo "⚠️ Skipping commit by Claude: $(git log --oneline -1 $commit)"
|
||||
continue
|
||||
fi
|
||||
|
||||
echo "Processing: $(git log --oneline -1 $commit)"
|
||||
|
||||
# Get the commit message and author info
|
||||
COMMIT_MSG=$(git log --format="%B" -n 1 $commit)
|
||||
COMMIT_AUTHOR=$(git log --format="%an <%ae>" -n 1 $commit)
|
||||
COMMIT_DATE=$(git log --format="%ad" -n 1 $commit)
|
||||
|
||||
# Apply the changes from this commit
|
||||
if git diff-tree --no-commit-id --name-only -r $commit | xargs -I {} git show $commit:{} > /dev/null 2>&1; then
|
||||
# Apply file changes
|
||||
git checkout $commit -- . || true
|
||||
|
||||
# Stage all changes
|
||||
git add -A
|
||||
|
||||
# Only commit if there are changes
|
||||
if ! git diff --cached --quiet; then
|
||||
# Create new commit with original metadata but new SHA
|
||||
GIT_AUTHOR_NAME=$(echo "$COMMIT_AUTHOR" | cut -d'<' -f1 | xargs)
|
||||
GIT_AUTHOR_EMAIL=$(echo "$COMMIT_AUTHOR" | cut -d'<' -f2 | cut -d'>' -f1)
|
||||
GIT_AUTHOR_DATE="$COMMIT_DATE"
|
||||
|
||||
export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL GIT_AUTHOR_DATE
|
||||
git commit -m "$COMMIT_MSG"
|
||||
echo "✅ Applied changes as new commit: $(git log --oneline -1)"
|
||||
else
|
||||
echo "⚠️ No changes to commit for $commit"
|
||||
fi
|
||||
else
|
||||
echo "⚠️ Skipping problematic commit $commit"
|
||||
fi
|
||||
done
|
||||
|
||||
echo "✅ Finished creating new history"
|
||||
else
|
||||
echo "✅ No commits after target commit - history starts fresh"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "=== New History Summary ==="
|
||||
echo "Total commits in new history: $(git rev-list --count github-mirror)"
|
||||
echo "History starts with: $(git log --oneline --reverse | head -1)"
|
||||
echo "Latest commit: $(git log --oneline -1)"
|
||||
|
||||
- name: Remove sensitive files and directories
|
||||
run: |
|
||||
# Switch to the github-mirror branch
|
||||
git checkout github-mirror
|
||||
|
||||
echo "Current files before cleanup:"
|
||||
ls -la | head -10 || true
|
||||
echo "..."
|
||||
@@ -164,18 +62,6 @@ jobs:
|
||||
echo "Final file structure (top level):"
|
||||
ls -la | head -10 || true
|
||||
|
||||
- name: Verify completely new history
|
||||
run: |
|
||||
git checkout github-mirror
|
||||
echo "=== Final History Verification ==="
|
||||
echo "Total commits in new github-mirror branch: $(git rev-list --count github-mirror)"
|
||||
echo ""
|
||||
echo "Complete commit history (should start from target commit content):"
|
||||
git log --oneline --reverse
|
||||
echo ""
|
||||
echo "⚠️ Note: This is a completely NEW history with new commit SHAs"
|
||||
echo "🔍 Original target commit content preserved but with new commit ID"
|
||||
|
||||
- name: Check GitHub token
|
||||
env:
|
||||
GITHUBTOKEN: ${{ secrets.GITHUBTOKEN }}
|
||||
@@ -187,13 +73,10 @@ jobs:
|
||||
echo "GitHub token is available (length: ${#GITHUBTOKEN})"
|
||||
fi
|
||||
|
||||
- name: Force push completely new history to GitHub
|
||||
- name: Push to GitHub
|
||||
env:
|
||||
GITHUBTOKEN: ${{ secrets.GITHUBTOKEN }}
|
||||
run: |
|
||||
# Switch to github-mirror branch
|
||||
git checkout github-mirror
|
||||
|
||||
# Remove existing github remote if it exists
|
||||
git remote remove github || true
|
||||
|
||||
@@ -204,17 +87,13 @@ jobs:
|
||||
echo "GitHub remote added:"
|
||||
git remote -v
|
||||
|
||||
# Force push the completely new history to GitHub main
|
||||
echo "🔥 FORCE PUSHING completely new history to GitHub..."
|
||||
echo "⚠️ This will COMPLETELY REPLACE all history on GitHub!"
|
||||
git push github github-mirror:main --force
|
||||
echo "✅ Force push completed - GitHub now has completely new history!"
|
||||
# Push to GitHub main branch
|
||||
echo "Pushing to GitHub..."
|
||||
git push github main --force
|
||||
echo "✅ Push to GitHub completed!"
|
||||
|
||||
- name: Workflow completed
|
||||
run: |
|
||||
echo "✅ Mirror to GitHub workflow completed successfully!"
|
||||
echo "🔥 COMPLETE HISTORY REPLACEMENT: GitHub now has entirely new history"
|
||||
echo "📊 History starts from commit content: 7aca927937"
|
||||
echo "🔍 Check https://github.com/the-luap/picpeak to verify the new history"
|
||||
echo "📈 Total commits pushed: $(git rev-list --count github-mirror)"
|
||||
echo "🆕 All commit SHAs are NEW - no connection to previous history"
|
||||
echo "📊 Repository mirrored to: https://github.com/the-luap/picpeak"
|
||||
echo "🔒 Sensitive files have been removed from the mirror"
|
||||
@@ -447,9 +447,14 @@ router.delete('/:eventId/photos/:photoId', adminAuth, async (req, res) => {
|
||||
if (photo.thumbnail_path) {
|
||||
const thumbPath = path.join(storagePath, 'events/active', photo.thumbnail_path);
|
||||
try {
|
||||
// Check if file exists before attempting to delete
|
||||
await fs.access(thumbPath);
|
||||
await fs.unlink(thumbPath);
|
||||
} catch (error) {
|
||||
console.error('Error deleting thumbnail:', error);
|
||||
// Only log if it's not a "file not found" error
|
||||
if (error.code !== 'ENOENT') {
|
||||
console.error('Error deleting thumbnail:', error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -534,9 +539,14 @@ router.post('/:eventId/photos/bulk-delete', adminAuth, async (req, res) => {
|
||||
if (photo.thumbnail_path) {
|
||||
const thumbPath = path.join(storagePath, photo.thumbnail_path);
|
||||
try {
|
||||
// Check if file exists before attempting to delete
|
||||
await fs.access(thumbPath);
|
||||
await fs.unlink(thumbPath);
|
||||
} catch (error) {
|
||||
console.error('Error deleting thumbnail:', error);
|
||||
// Only log if it's not a "file not found" error
|
||||
if (error.code !== 'ENOENT') {
|
||||
console.error('Error deleting thumbnail:', error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Executable
+91
@@ -0,0 +1,91 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Script to clean git history - removes all commits before July 17, 2025
|
||||
# WARNING: This is destructive and will rewrite history!
|
||||
|
||||
set -e
|
||||
|
||||
echo "⚠️ WARNING: This script will permanently rewrite git history!"
|
||||
echo "⚠️ All commits before July 17, 2025 will be removed."
|
||||
echo "⚠️ This action cannot be undone!"
|
||||
echo ""
|
||||
read -p "Are you sure you want to continue? (type 'yes' to proceed): " confirmation
|
||||
|
||||
if [ "$confirmation" != "yes" ]; then
|
||||
echo "Operation cancelled."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Create backup branch
|
||||
echo "Creating backup branch..."
|
||||
git checkout -b backup-before-cleanup-$(date +%Y%m%d-%H%M%S)
|
||||
git checkout main
|
||||
|
||||
# Find the first commit on or after July 17, 2025
|
||||
echo "Finding first commit after July 17, 2025..."
|
||||
FIRST_COMMIT=$(git log --since="2025-07-17" --reverse --format="%H" | head -1)
|
||||
|
||||
if [ -z "$FIRST_COMMIT" ]; then
|
||||
echo "ERROR: No commits found after July 17, 2025"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "First commit to keep: $FIRST_COMMIT"
|
||||
echo "Commit details: $(git log --oneline -1 $FIRST_COMMIT)"
|
||||
|
||||
# Get all commits we want to keep
|
||||
COMMITS_TO_KEEP=$(git log --since="2025-07-17" --reverse --format="%H")
|
||||
COMMIT_COUNT=$(echo "$COMMITS_TO_KEEP" | wc -l)
|
||||
echo "Total commits to preserve: $COMMIT_COUNT"
|
||||
|
||||
# Create new orphan branch
|
||||
echo "Creating new clean history..."
|
||||
git checkout --orphan new-main
|
||||
|
||||
# Clean the working directory
|
||||
git rm -rf . || true
|
||||
|
||||
# Get the tree from the first commit
|
||||
git checkout $FIRST_COMMIT -- .
|
||||
|
||||
# Create new initial commit with same content but new message
|
||||
ORIGINAL_MESSAGE=$(git log --format="%B" -1 $FIRST_COMMIT)
|
||||
ORIGINAL_AUTHOR=$(git log --format="%an <%ae>" -1 $FIRST_COMMIT)
|
||||
ORIGINAL_DATE=$(git log --format="%ad" -1 $FIRST_COMMIT)
|
||||
|
||||
GIT_AUTHOR_NAME=$(echo "$ORIGINAL_AUTHOR" | cut -d'<' -f1 | xargs)
|
||||
GIT_AUTHOR_EMAIL=$(echo "$ORIGINAL_AUTHOR" | cut -d'<' -f2 | cut -d'>' -f1)
|
||||
GIT_AUTHOR_DATE="$ORIGINAL_DATE"
|
||||
export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL GIT_AUTHOR_DATE
|
||||
|
||||
git add -A
|
||||
git commit -m "Initial commit - Project start (July 17, 2025)
|
||||
|
||||
Original: $ORIGINAL_MESSAGE"
|
||||
|
||||
# Cherry-pick remaining commits
|
||||
echo "Applying remaining commits..."
|
||||
REMAINING_COMMITS=$(git log --since="2025-07-17" --reverse --format="%H" $FIRST_COMMIT..main)
|
||||
|
||||
if [ -n "$REMAINING_COMMITS" ]; then
|
||||
for commit in $REMAINING_COMMITS; do
|
||||
echo "Applying: $(git log --oneline -1 $commit)"
|
||||
git cherry-pick $commit || {
|
||||
echo "ERROR: Failed to cherry-pick $commit"
|
||||
echo "You may need to resolve conflicts and continue manually"
|
||||
exit 1
|
||||
}
|
||||
done
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "✅ New history created successfully!"
|
||||
echo "Total commits in new history: $(git rev-list --count HEAD)"
|
||||
echo ""
|
||||
echo "To finalize the cleanup, run these commands:"
|
||||
echo " git branch -D main"
|
||||
echo " git branch -m main"
|
||||
echo " git push origin main --force"
|
||||
echo ""
|
||||
echo "⚠️ WARNING: Force pushing will overwrite the remote repository!"
|
||||
echo "⚠️ Make sure you have a backup and all team members are aware!"
|
||||
@@ -23,7 +23,7 @@ export const AdminPhotoGrid: React.FC<AdminPhotoGridProps> = ({
|
||||
const [selectedPhotos, setSelectedPhotos] = useState<Set<number>>(new Set());
|
||||
const [isSelectionMode, setIsSelectionMode] = useState(false);
|
||||
const [isDeleting, setIsDeleting] = useState(false);
|
||||
const [deletingPhotoId, setDeletingPhotoId] = useState<number | null>(null);
|
||||
const [deletingPhotos, setDeletingPhotos] = useState<Set<number>>(new Set());
|
||||
|
||||
const handlePhotoSelect = (photoId: number, e?: React.MouseEvent) => {
|
||||
if (e) {
|
||||
@@ -54,15 +54,18 @@ export const AdminPhotoGrid: React.FC<AdminPhotoGridProps> = ({
|
||||
return;
|
||||
}
|
||||
|
||||
setDeletingPhotoId(photo.id);
|
||||
setDeletingPhotos(prev => new Set(prev).add(photo.id));
|
||||
try {
|
||||
await photosService.deletePhoto(eventId, photo.id);
|
||||
toast.success('Photo deleted successfully');
|
||||
onPhotosDeleted();
|
||||
} catch (error) {
|
||||
toast.error('Failed to delete photo');
|
||||
} finally {
|
||||
setDeletingPhotoId(null);
|
||||
setDeletingPhotos(prev => {
|
||||
const newSet = new Set(prev);
|
||||
newSet.delete(photo.id);
|
||||
return newSet;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -75,14 +78,18 @@ export const AdminPhotoGrid: React.FC<AdminPhotoGridProps> = ({
|
||||
}
|
||||
|
||||
setIsDeleting(true);
|
||||
const selectedIds = Array.from(selectedPhotos);
|
||||
setDeletingPhotos(new Set(selectedIds));
|
||||
|
||||
try {
|
||||
await photosService.deletePhotos(eventId, Array.from(selectedPhotos));
|
||||
await photosService.deletePhotos(eventId, selectedIds);
|
||||
toast.success(`${count} photo${count > 1 ? 's' : ''} deleted successfully`);
|
||||
setSelectedPhotos(new Set());
|
||||
setIsSelectionMode(false);
|
||||
onPhotosDeleted();
|
||||
} catch (error) {
|
||||
toast.error('Failed to delete photos');
|
||||
setDeletingPhotos(new Set());
|
||||
} finally {
|
||||
setIsDeleting(false);
|
||||
}
|
||||
@@ -155,13 +162,15 @@ export const AdminPhotoGrid: React.FC<AdminPhotoGridProps> = ({
|
||||
|
||||
{/* Photo Grid */}
|
||||
<div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5 gap-4">
|
||||
{photos.map((photo, index) => (
|
||||
<div
|
||||
key={photo.id}
|
||||
className={`relative group cursor-pointer rounded-lg overflow-hidden bg-neutral-100 ${
|
||||
isSelectionMode ? 'ring-2 ring-offset-2 ' + (selectedPhotos.has(photo.id) ? 'ring-primary-500' : 'ring-transparent') : ''
|
||||
}`}
|
||||
onClick={() => isSelectionMode ? handlePhotoSelect(photo.id) : onPhotoClick(photo, index)}
|
||||
{photos.map((photo, index) => {
|
||||
const isDeleting = deletingPhotos.has(photo.id);
|
||||
return (
|
||||
<div
|
||||
key={photo.id}
|
||||
className={`relative group cursor-pointer rounded-lg overflow-hidden bg-neutral-100 transition-opacity ${
|
||||
isSelectionMode ? 'ring-2 ring-offset-2 ' + (selectedPhotos.has(photo.id) ? 'ring-primary-500' : 'ring-transparent') : ''
|
||||
} ${isDeleting ? 'opacity-50' : ''}`}
|
||||
onClick={() => !isDeleting && (isSelectionMode ? handlePhotoSelect(photo.id) : onPhotoClick(photo, index))}
|
||||
>
|
||||
{/* Selection Checkbox */}
|
||||
{isSelectionMode && (
|
||||
@@ -219,8 +228,8 @@ export const AdminPhotoGrid: React.FC<AdminPhotoGridProps> = ({
|
||||
</button>
|
||||
<button
|
||||
onClick={(e) => handleDeleteSingle(photo, e)}
|
||||
className="p-1 text-white hover:bg-white/20 rounded"
|
||||
disabled={deletingPhotoId === photo.id}
|
||||
className="p-1 text-white hover:bg-white/20 rounded disabled:opacity-50"
|
||||
disabled={isDeleting}
|
||||
>
|
||||
<Trash2 className="w-3 h-3" />
|
||||
</button>
|
||||
@@ -238,7 +247,8 @@ export const AdminPhotoGrid: React.FC<AdminPhotoGridProps> = ({
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
{photos.length === 0 && (
|
||||
|
||||
@@ -256,7 +256,7 @@ export const PhotoUpload: React.FC<PhotoUploadProps> = ({ eventId, onUploadCompl
|
||||
<div className="flex justify-between text-sm text-neutral-600 mb-1">
|
||||
<span>
|
||||
{t('upload.uploading')}
|
||||
{totalChunks > 1 && ` (${t('common.chunk') || 'Chunk'} ${currentChunk}/${totalChunks})`}
|
||||
{totalChunks > 1 && ` (${t('common.chunk')} ${currentChunk}/${totalChunks})`}
|
||||
</span>
|
||||
<span>{uploadProgress}%</span>
|
||||
</div>
|
||||
@@ -268,7 +268,7 @@ export const PhotoUpload: React.FC<PhotoUploadProps> = ({ eventId, onUploadCompl
|
||||
</div>
|
||||
{totalChunks > 1 && (
|
||||
<p className="text-xs text-neutral-500 mt-1">
|
||||
{t('upload.uploadingChunks') || `Uploading ${selectedFiles.length} files in ${totalChunks} batches...`}
|
||||
{t('upload.uploadingChunks', { count: selectedFiles.length, total: totalChunks })}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -35,7 +35,8 @@
|
||||
"days": "Tage",
|
||||
"customize": "Anpassen",
|
||||
"hide": "Ausblenden",
|
||||
"unknown": "Unbekannt"
|
||||
"unknown": "Unbekannt",
|
||||
"chunk": "Teil"
|
||||
},
|
||||
"upload": {
|
||||
"photoCategory": "Fotokategorie",
|
||||
@@ -51,7 +52,8 @@
|
||||
"uploadPhotos": "Fotos hochladen",
|
||||
"maxFilesReached": "Maximal 500 Dateien erlaubt",
|
||||
"someFilesSkipped": "Einige Dateien wurden übersprungen (500 Dateien Limit)",
|
||||
"tooManyFiles": "Maximal 500 Dateien können gleichzeitig hochgeladen werden"
|
||||
"tooManyFiles": "Maximal 500 Dateien können gleichzeitig hochgeladen werden",
|
||||
"uploadingChunks": "Lade {{count}} Dateien in {{total}} Teilen hoch..."
|
||||
},
|
||||
"navigation": {
|
||||
"dashboard": "Dashboard",
|
||||
|
||||
@@ -35,7 +35,8 @@
|
||||
"days": "days",
|
||||
"customize": "Customize",
|
||||
"hide": "Hide",
|
||||
"unknown": "Unknown"
|
||||
"unknown": "Unknown",
|
||||
"chunk": "Chunk"
|
||||
},
|
||||
"upload": {
|
||||
"photoCategory": "Photo Category",
|
||||
@@ -51,7 +52,8 @@
|
||||
"uploadPhotos": "Upload Photos",
|
||||
"maxFilesReached": "Maximum 500 files allowed",
|
||||
"someFilesSkipped": "Some files were skipped (500 file limit)",
|
||||
"tooManyFiles": "Maximum 500 files can be uploaded at once"
|
||||
"tooManyFiles": "Maximum 500 files can be uploaded at once",
|
||||
"uploadingChunks": "Uploading {{count}} files in {{total}} batches..."
|
||||
},
|
||||
"navigation": {
|
||||
"dashboard": "Dashboard",
|
||||
|
||||
Reference in New Issue
Block a user