Compare commits

...

4 Commits

Author SHA1 Message Date
Daniel Luiz Alves
f2a0e60f20 [RELEASE] v3.1.7-beta (#181) 2025-07-29 22:54:19 -03:00
Daniel Luiz Alves
6cb21e95c4 chore: increment version numbers to 3.1.7-beta across all package.json files 2025-07-29 22:20:56 -03:00
Daniel Luiz Alves
868add68a5 feat: enhance localization with new placeholders and error messages
- Added "namePlaceholder" to share creation modals across multiple languages for improved user guidance.
- Updated error messages in various languages to maintain consistency and clarity.
- Introduced "filesQueued" message for better user feedback during file uploads in multiple languages.
2025-07-29 22:16:07 -03:00
Daniel Luiz Alves
307148d951 fix: enhance reset-password script and update navbar responsiveness
- Updated reset-password.sh to set DATABASE_URL if not already defined and ensure the database directory exists.
- Improved navbar component responsiveness by adjusting visibility classes for navigation items and added a new sponsor link.
2025-07-29 18:37:22 -03:00
25 changed files with 167 additions and 58 deletions

View File

@@ -1,6 +1,6 @@
{
"name": "palmr-docs",
"version": "3.1.6-beta",
"version": "3.1.7-beta",
"description": "Docs for Palmr",
"private": true,
"author": "Daniel Luiz Alves <daniel@kyantech.com.br>",

View File

@@ -1,6 +1,6 @@
{
"name": "palmr-api",
"version": "3.1.6-beta",
"version": "3.1.7-beta",
"description": "API for Palmr",
"private": true,
"author": "Daniel Luiz Alves <daniel@kyantech.com.br>",

View File

@@ -6,7 +6,7 @@
echo "🔐 Palmr Password Reset Tool"
echo "============================="
# Check if we're in the right directory
# Check if we're in the right directory and set DATABASE_URL
if [ ! -f "package.json" ]; then
echo "❌ Error: This script must be run from the server directory (/app/server)"
echo " Current directory: $(pwd)"
@@ -14,18 +14,26 @@ if [ ! -f "package.json" ]; then
exit 1
fi
# Set DATABASE_URL if not already set
if [ -z "$DATABASE_URL" ]; then
export DATABASE_URL="file:/app/server/prisma/palmr.db"
fi
# Ensure database directory exists
mkdir -p /app/server/prisma
# Function to check if tsx is available
check_tsx() {
# Check if tsx binary exists in node_modules
if [ -f "node_modules/.bin/tsx" ]; then
return 0
fi
# Fallback: try npx
if npx tsx --version >/dev/null 2>&1; then
return 0
fi
return 1
}
@@ -39,7 +47,7 @@ install_tsx_only() {
else
return 1
fi
return $?
}
@@ -62,7 +70,7 @@ ensure_prisma() {
if [ -d "node_modules/@prisma/client" ] && [ -f "node_modules/@prisma/client/index.js" ]; then
return 0
fi
echo "📦 Generating Prisma client..."
if npx prisma generate --silent >/dev/null 2>&1; then
echo "✅ Prisma client ready"
@@ -81,14 +89,14 @@ if check_tsx; then
echo "✅ tsx is ready"
else
echo "📦 tsx not found, installing..."
# Try quick tsx-only install first
if install_tsx_only && check_tsx; then
echo "✅ tsx installed successfully"
else
echo "⚠️ Quick install failed, installing all dependencies..."
install_all_deps
# Final check
if ! check_tsx; then
echo "❌ Error: tsx is still not available after full installation"
@@ -119,4 +127,4 @@ if [ -f "node_modules/.bin/tsx" ]; then
node_modules/.bin/tsx src/scripts/reset-password.ts "$@"
else
npx tsx src/scripts/reset-password.ts "$@"
fi
fi

View File

@@ -159,7 +159,8 @@
"passwordLabel": "كلمة المرور",
"create": "إنشاء مشاركة",
"success": "تم إنشاء المشاركة بنجاح",
"error": "فشل في إنشاء المشاركة"
"error": "فشل في إنشاء المشاركة",
"namePlaceholder": "أدخل اسمًا لمشاركتك"
},
"dashboard": {
"loadError": "فشل في تحميل بيانات لوحة التحكم",
@@ -1659,7 +1660,8 @@
"title": "إفلات الملفات للرفع",
"description": "حرر للرفع ملفاتك"
},
"pasteSuccess": "{count, plural, =1 {تم لصق الصورة ورفعها بنجاح} other {تم لصق # صور ورفعها بنجاح}}"
"pasteSuccess": "{count, plural, =1 {تم لصق الصورة ورفعها بنجاح} other {تم لصق # صور ورفعها بنجاح}}",
"filesQueued": "{count, plural, one {# ملف في الصف} other {# ملفات في الصف}}"
},
"users": {
"modes": {

View File

@@ -159,7 +159,8 @@
"passwordLabel": "Passwort",
"create": "Freigabe Erstellen",
"success": "Freigabe erfolgreich erstellt",
"error": "Fehler beim Erstellen der Freigabe"
"error": "Fehler beim Erstellen der Freigabe",
"namePlaceholder": "Geben Sie einen Namen für Ihre Freigabe ein"
},
"dashboard": {
"loadError": "Fehler beim Laden der Dashboard-Daten",
@@ -1657,7 +1658,8 @@
"title": "Dateien zum Hochladen ablegen",
"description": "Loslassen, um Ihre Dateien hochzuladen"
},
"pasteSuccess": "{count, plural, =1 {Bild erfolgreich eingefügt und hochgeladen} other {# Bilder erfolgreich eingefügt und hochgeladen}}"
"pasteSuccess": "{count, plural, =1 {Bild erfolgreich eingefügt und hochgeladen} other {# Bilder erfolgreich eingefügt und hochgeladen}}",
"filesQueued": "{count, plural, one {# Datei in der Warteschlange} other {# Dateien in der Warteschlange}}"
},
"users": {
"modes": {

View File

@@ -149,6 +149,7 @@
"createShare": {
"title": "Create Share",
"nameLabel": "Share Name",
"namePlaceholder": "Enter a name for your share",
"descriptionLabel": "Description",
"descriptionPlaceholder": "Enter a description (optional)",
"expirationLabel": "Expiration Date",
@@ -1634,6 +1635,7 @@
"selectFile": "Click to select a file",
"selectMultipleFiles": "Click to select one or multiple files",
"dragAndDrop": "or drag and drop files here",
"filesQueued": "{count, plural, one {# file queued for upload} other {# files queued for upload}}",
"preview": "Preview",
"uploadProgress": "Upload progress",
"upload": "Upload",
@@ -1745,4 +1747,4 @@
"nameRequired": "Name is required",
"required": "This field is required"
}
}
}

View File

@@ -159,7 +159,8 @@
"passwordLabel": "Contraseña",
"create": "Crear Compartir",
"success": "Compartir creado exitosamente",
"error": "Error al crear compartir"
"error": "Error al crear compartir",
"namePlaceholder": "Ingrese un nombre para su compartir"
},
"dashboard": {
"loadError": "Error al cargar los datos del tablero",
@@ -1657,7 +1658,8 @@
"title": "Suelta archivos para subir",
"description": "Suelta para subir tus archivos"
},
"pasteSuccess": "{count, plural, =1 {Imagen pegada y subida exitosamente} other {# imágenes pegadas y subidas exitosamente}}"
"pasteSuccess": "{count, plural, =1 {Imagen pegada y subida exitosamente} other {# imágenes pegadas y subidas exitosamente}}",
"filesQueued": "{count, plural, one {# archivo en cola para subir} other {# archivos en cola para subir}}"
},
"users": {
"modes": {

View File

@@ -159,7 +159,8 @@
"passwordLabel": "Mot de Passe",
"create": "Créer un Partage",
"success": "Partage créé avec succès",
"error": "Échec de la création du partage"
"error": "Échec de la création du partage",
"namePlaceholder": "Entrez un nom pour votre partage"
},
"dashboard": {
"loadError": "Échec du chargement des données du tableau de bord",
@@ -1657,7 +1658,8 @@
"title": "Déposer des fichiers pour télécharger",
"description": "Relâchez pour télécharger vos fichiers"
},
"pasteSuccess": "{count, plural, =1 {Image collée et téléchargée avec succès} other {# images collées et téléchargées avec succès}}"
"pasteSuccess": "{count, plural, =1 {Image collée et téléchargée avec succès} other {# images collées et téléchargées avec succès}}",
"filesQueued": "{count, plural, one {# fichier en attente de téléchargement} other {# fichiers en attente de téléchargement}}"
},
"users": {
"modes": {

View File

@@ -159,7 +159,8 @@
"passwordLabel": "पासवर्ड",
"create": "साझाकरण बनाएं",
"success": "साझाकरण सफलतापूर्वक बनाया गया",
"error": "साझाकरण बनाने में विफल"
"error": "साझाकरण बनाने में विफल",
"namePlaceholder": "अपने साझाकरण के लिए एक नाम दर्ज करें"
},
"dashboard": {
"loadError": "डैशबोर्ड डेटा लोड करने में त्रुटि",
@@ -1657,7 +1658,8 @@
"title": "अपलोड करने के लिए फ़ाइलें छोड़ें",
"description": "अपनी फ़ाइलें अपलोड करने के लिए छोड़ें"
},
"pasteSuccess": "{count, plural, =1 {छवि सफलतापूर्वक चिपकाई और अपलोड की गई} other {# छवियाँ सफलतापूर्वक चिपकाई और अपलोड की गईं}}"
"pasteSuccess": "{count, plural, =1 {छवि सफलतापूर्वक चिपकाई और अपलोड की गई} other {# छवियाँ सफलतापूर्वक चिपकाई और अपलोड की गईं}}",
"filesQueued": "{count, plural, one {# फ़ाइल अपलोड के लिए इंतजार में है} other {# फ़ाइलें अपलोड के लिए इंतजार में हैं}}"
},
"users": {
"modes": {

View File

@@ -159,7 +159,8 @@
"passwordLabel": "Password",
"create": "Crea Condivisione",
"success": "Condivisione creata con successo",
"error": "Errore nella creazione della condivisione"
"error": "Errore nella creazione della condivisione",
"namePlaceholder": "Inserisci un nome per la tua condivisione"
},
"dashboard": {
"loadError": "Errore durante il caricamento dei dati del pannello di controllo",
@@ -1657,7 +1658,8 @@
"title": "Rilascia i file per caricarli",
"description": "Rilascia per caricare i tuoi file"
},
"pasteSuccess": "{count, plural, =1 {Immagine incollata e caricata con successo} other {# immagini incollate e caricate con successo}}"
"pasteSuccess": "{count, plural, =1 {Immagine incollata e caricata con successo} other {# immagini incollate e caricate con successo}}",
"filesQueued": "{count, plural, one {# file in coda per il caricamento} other {# files in coda per il caricamento}}"
},
"users": {
"modes": {

View File

@@ -159,7 +159,8 @@
"passwordLabel": "パスワード",
"create": "共有を作成",
"success": "共有が正常に作成されました",
"error": "共有の作成に失敗しました"
"error": "共有の作成に失敗しました",
"namePlaceholder": "共有の名前を入力してください"
},
"dashboard": {
"loadError": "ダッシュボードデータの読み込みに失敗しました",
@@ -1657,7 +1658,8 @@
"title": "アップロードするファイルをドロップ",
"description": "ファイルをアップロードするにはリリースしてください"
},
"pasteSuccess": "{count, plural, =1 {画像が貼り付けられ、正常にアップロードされました} other {#個の画像が貼り付けられ、正常にアップロードされました}}"
"pasteSuccess": "{count, plural, =1 {画像が貼り付けられ、正常にアップロードされました} other {#個の画像が貼り付けられ、正常にアップロードされました}}",
"filesQueued": "{count, plural, one {# ファイルがアップロード待ち} other {# ファイルがアップロード待ち}}"
},
"users": {
"modes": {

View File

@@ -159,7 +159,8 @@
"passwordLabel": "비밀번호",
"create": "공유 생성",
"success": "공유가 성공적으로 생성되었습니다",
"error": "공유 생성에 실패했습니다"
"error": "공유 생성에 실패했습니다",
"namePlaceholder": "공유 이름을 입력하세요"
},
"dashboard": {
"loadError": "대시보드 데이터를 불러오는데 실패했습니다",
@@ -1657,7 +1658,8 @@
"title": "업로드할 파일 드롭",
"description": "파일을 업로드하려면 놓으세요"
},
"pasteSuccess": "{count, plural, =1 {이미지가 성공적으로 붙여넣기 및 업로드되었습니다} other {# 개의 이미지가 성공적으로 붙여넣기 및 업로드되었습니다}}"
"pasteSuccess": "{count, plural, =1 {이미지가 성공적으로 붙여넣기 및 업로드되었습니다} other {# 개의 이미지가 성공적으로 붙여넣기 및 업로드되었습니다}}",
"filesQueued": "{count, plural, one {# 파일이 업로드 대기 중} other {# 파일이 업로드 대기 중}}"
},
"users": {
"modes": {

View File

@@ -159,7 +159,8 @@
"passwordLabel": "Wachtwoord",
"create": "Delen Maken",
"success": "Delen succesvol aangemaakt",
"error": "Fout bij het aanmaken van delen"
"error": "Fout bij het aanmaken van delen",
"namePlaceholder": "Voer een naam in voor uw delen"
},
"dashboard": {
"loadError": "Fout bij het laden van controlepaneel gegevens",
@@ -1657,7 +1658,8 @@
"title": "Sleep bestanden om te uploaden",
"description": "Laat los om je bestanden te uploaden"
},
"pasteSuccess": "{count, plural, =1 {Afbeelding geplakt en succesvol geüpload} other {# afbeeldingen geplakt en succesvol geüpload}}"
"pasteSuccess": "{count, plural, =1 {Afbeelding geplakt en succesvol geüpload} other {# afbeeldingen geplakt en succesvol geüpload}}",
"filesQueued": "{count, plural, one {# bestand in de wachtrij voor upload} other {# bestanden in de wachtrij voor upload}}"
},
"users": {
"modes": {

View File

@@ -159,7 +159,8 @@
"passwordLabel": "Hasło",
"create": "Utwórz Udostępnienie",
"success": "Udostępnienie utworzone pomyślnie",
"error": "Nie udało się utworzyć udostępnienia"
"error": "Nie udało się utworzyć udostępnienia",
"namePlaceholder": "Wprowadź nazwę dla swojego udostępnienia"
},
"dashboard": {
"loadError": "Nie udało się załadować danych panelu głównego",
@@ -1657,7 +1658,8 @@
"title": "Upuść pliki, aby przesłać",
"description": "Zwolnij, aby przesłać pliki"
},
"pasteSuccess": "{count, plural, =1 {Obraz wklejony i przesłany pomyślnie} other {# obrazy wklejone i przesłane pomyślnie}}"
"pasteSuccess": "{count, plural, =1 {Obraz wklejony i przesłany pomyślnie} other {# obrazy wklejone i przesłane pomyślnie}}",
"filesQueued": "{count, plural, one {# plik w kolejce do przesyłania} other {# pliki w kolejce do przesyłania}}"
},
"users": {
"modes": {

View File

@@ -159,7 +159,8 @@
"passwordLabel": "Senha",
"create": "Criar compartilhamento",
"success": "Compartilhamento criado com sucesso",
"error": "Falha ao criar compartilhamento"
"error": "Falha ao criar compartilhamento",
"namePlaceholder": "Digite um nome para seu compartilhamento"
},
"dashboard": {
"loadError": "Falha ao carregar dados do painel",
@@ -643,7 +644,7 @@
"edit": "Editar",
"delete": "Excluir",
"viewFiles": "Arquivos Recebidos",
"viewQrCode": "[TO_TRANSLATE] View QR Code"
"viewQrCode": "Ver QR Code"
},
"empty": {
"title": "Nenhum link de recebimento criado",
@@ -1657,7 +1658,8 @@
"continue": "Continuar Uploads",
"cancel": "Cancelar Uploads"
},
"pasteSuccess": "{count, plural, =1 {Imagem colada e enviada com sucesso} other {# imagens coladas e enviadas com sucesso}}"
"pasteSuccess": "{count, plural, =1 {Imagem colada e enviada com sucesso} other {# imagens coladas e enviadas com sucesso}}",
"filesQueued": "{count, plural, one {# arquivo na fila para upload} other {# arquivos na fila para upload}}"
},
"users": {
"modes": {

View File

@@ -159,7 +159,8 @@
"success": "Общий доступ успешно создан",
"error": "Не удалось создать общий доступ",
"descriptionLabel": "Описание",
"descriptionPlaceholder": "Введите описание (опционально)"
"descriptionPlaceholder": "Введите описание (опционально)",
"namePlaceholder": "Введите имя для вашего общего доступа"
},
"dashboard": {
"loadError": "Ошибка загрузки данных панели управления",
@@ -1657,7 +1658,8 @@
"title": "Перетащите файлы для загрузки",
"description": "Отпустите, чтобы загрузить файлы"
},
"pasteSuccess": "{count, plural, =1 {Изображение вставлено и успешно загружено} other {# изображений вставлено и успешно загружено}}"
"pasteSuccess": "{count, plural, =1 {Изображение вставлено и успешно загружено} other {# изображений вставлено и успешно загружено}}",
"filesQueued": "{count, plural, one {# файл в очереди для загрузки} other {# файлов в очереди для загрузки}}"
},
"users": {
"modes": {

View File

@@ -159,7 +159,8 @@
"success": "Paylaşım başarıyla oluşturuldu",
"error": "Paylaşım oluşturulamadı",
"descriptionLabel": "Açıklama",
"descriptionPlaceholder": "Açıklama girin (isteğe bağlı)"
"descriptionPlaceholder": "Açıklama girin (isteğe bağlı)",
"namePlaceholder": "Paylaşımınız için bir ad girin"
},
"dashboard": {
"loadError": "Gösterge paneli verileri yüklenemedi",
@@ -1657,7 +1658,8 @@
"title": "Yüklemek için dosyaları bırakın",
"description": "Dosyalarınızı yüklemek için bırakın"
},
"pasteSuccess": "{count, plural, =1 {Görüntü yapıştırıldı ve başarıyla yüklendi} other {# görüntü yapıştırıldı ve başarıyla yüklendi}}"
"pasteSuccess": "{count, plural, =1 {Görüntü yapıştırıldı ve başarıyla yüklendi} other {# görüntü yapıştırıldı ve başarıyla yüklendi}}",
"filesQueued": "{count, plural, one {# dosya yükleme için bekliyor} other {# dosya yükleme için bekliyor}}"
},
"users": {
"modes": {

View File

@@ -159,7 +159,8 @@
"passwordLabel": "密码",
"create": "创建分享",
"success": "分享创建成功",
"error": "创建分享失败"
"error": "创建分享失败",
"namePlaceholder": "输入分享名称"
},
"dashboard": {
"loadError": "加载仪表盘数据失败",
@@ -1657,7 +1658,8 @@
"title": "拖放文件以上传",
"description": "松开以上传您的文件"
},
"pasteSuccess": "{count, plural, =1 {图片已成功粘贴并上传} other {# 张图片已成功粘贴并上传}}"
"pasteSuccess": "{count, plural, =1 {图片已成功粘贴并上传} other {# 张图片已成功粘贴并上传}}",
"filesQueued": "{count, plural, one {# 文件正在等待上传} other {# 文件正在等待上传}}"
},
"users": {
"modes": {

View File

@@ -1,6 +1,6 @@
{
"name": "palmr-web",
"version": "3.1.6-beta",
"version": "3.1.7-beta",
"description": "Frontend for Palmr",
"private": true,
"author": "Daniel Luiz Alves <daniel@kyantech.com.br>",

View File

@@ -29,7 +29,7 @@ export function Navbar() {
{appLogo && <img alt="App Logo" className="h-8 w-8 object-contain rounded" src={appLogo} />}
<p className="font-bold text-2xl">{appName}</p>
</Link>
<nav className="hidden lg:flex ml-2 gap-4">
<nav className="hidden md:flex ml-2 gap-4">
{siteConfig.navItems.map((item) => (
<Link
key={item.href}
@@ -44,7 +44,7 @@ export function Navbar() {
))}
</nav>
</div>
<div className="hidden md:flex items-center gap-2">
<div className="hidden lg:flex items-center gap-2">
<LanguageSwitcher />
<ModeToggle />
@@ -56,7 +56,7 @@ export function Navbar() {
</Button>
</div>
<div className="flex items-center gap-2 sm:hidden">
<div className="flex items-center gap-2 md:hidden">
<LanguageSwitcher />
<ModeToggle />
<Sheet open={isMenuOpen} onOpenChange={setIsMenuOpen}>
@@ -79,6 +79,16 @@ export function Navbar() {
{item.label}
</Link>
))}
<Link
href={siteConfig.links.sponsor}
target="_blank"
rel="noopener noreferrer"
className="text-foreground text-lg font-medium flex items-center gap-2"
onClick={() => setIsMenuOpen(false)}
>
<IconHeart className="h-4 w-4 text-destructive" />
Sponsor
</Link>
</div>
</div>
</SheetContent>

View File

@@ -1,6 +1,6 @@
"use client";
import { useEffect, type ReactNode } from "react";
import { useEffect, useState, type ReactNode } from "react";
import { useRouter } from "next/navigation";
import { useAuth } from "@/contexts/auth-context";
@@ -14,16 +14,21 @@ type ProtectedRouteProps = {
export function ProtectedRoute({ children, requireAdmin = false }: ProtectedRouteProps) {
const { isAuthenticated, isAdmin } = useAuth();
const router = useRouter();
const [hasCheckedAuth, setHasCheckedAuth] = useState(false);
useEffect(() => {
if (isAuthenticated === false) {
router.replace("/login");
} else if (requireAdmin && isAdmin === false) {
router.replace("/dashboard");
if (isAuthenticated !== null) {
setHasCheckedAuth(true);
if (isAuthenticated === false) {
router.replace("/login");
} else if (requireAdmin && isAdmin === false) {
router.replace("/dashboard");
}
}
}, [isAuthenticated, isAdmin, requireAdmin, router]);
if (isAuthenticated === null || (requireAdmin && isAdmin === null)) {
if (!hasCheckedAuth || isAuthenticated === null || (requireAdmin && isAdmin === null)) {
return <LoadingScreen />;
}

View File

@@ -1,5 +1,6 @@
"use client";
import { useState } from "react";
import Link from "next/link";
import { useRouter } from "next/navigation";
import { IconLogout, IconSettings, IconUser, IconUsers } from "@tabler/icons-react";
@@ -21,8 +22,22 @@ import { logout as logoutAPI } from "@/http/endpoints";
export function Navbar() {
const t = useTranslations();
const router = useRouter();
const { user, isAdmin, logout } = useAuth();
const { user, isAdmin, logout, isAuthenticated } = useAuth();
const { appName, appLogo } = useAppInfo();
const [isNavigating, setIsNavigating] = useState(false);
const handleLogoClick = async () => {
if (isNavigating || !isAuthenticated) return;
try {
setIsNavigating(true);
router.replace("/dashboard");
} catch (err) {
console.error("Error navigating to dashboard:", err);
} finally {
setTimeout(() => setIsNavigating(false), 500);
}
};
const handleLogout = async () => {
try {
@@ -39,10 +54,15 @@ export function Navbar() {
<div className="container flex h-16 max-w-screen-xl items-center mx-auto lg:px-6">
<div className="flex flex-1 items-center justify-between">
<div className="flex items-center gap-3">
<Link href="/dashboard" className="flex items-center gap-2 cursor-pointer">
<div
onClick={handleLogoClick}
className={`flex items-center gap-2 cursor-pointer transition-opacity ${
isNavigating ? "opacity-50" : "opacity-100"
}`}
>
{appLogo && <img alt={t("navbar.logoAlt")} className="h-8 w-8 object-contain rounded" src={appLogo} />}
<p className="font-bold text-2xl">{appName}</p>
</Link>
</div>
</div>
<div className="flex items-center gap-2 cursor-pointer">

View File

@@ -37,7 +37,15 @@ export function CreateShareModal({ isOpen, onClose, onSuccess }: CreateShareModa
name: formData.name,
description: formData.description || undefined,
password: formData.isPasswordProtected ? formData.password : undefined,
expiration: formData.expiresAt ? new Date(formData.expiresAt).toISOString() : undefined,
expiration: formData.expiresAt
? (() => {
const dateValue = formData.expiresAt;
if (dateValue.length === 10) {
return new Date(dateValue + "T23:59:59").toISOString();
}
return new Date(dateValue).toISOString();
})()
: undefined,
maxViews: formData.maxViews ? parseInt(formData.maxViews) : undefined,
files: [],
});
@@ -71,7 +79,16 @@ export function CreateShareModal({ isOpen, onClose, onSuccess }: CreateShareModa
<div className="flex flex-col gap-4">
<div className="space-y-2">
<Label>{t("createShare.nameLabel")}</Label>
<Input value={formData.name} onChange={(e) => setFormData({ ...formData, name: e.target.value })} />
<Input
value={formData.name}
onChange={(e) => setFormData({ ...formData, name: e.target.value })}
onPaste={(e) => {
e.preventDefault();
const pastedText = e.clipboardData.getData("text");
setFormData({ ...formData, name: pastedText });
}}
placeholder={t("createShare.namePlaceholder")}
/>
</div>
<div className="space-y-2">
@@ -93,6 +110,12 @@ export function CreateShareModal({ isOpen, onClose, onSuccess }: CreateShareModa
type="datetime-local"
value={formData.expiresAt}
onChange={(e) => setFormData({ ...formData, expiresAt: e.target.value })}
onBlur={(e) => {
const value = e.target.value;
if (value && value.length === 10) {
setFormData({ ...formData, expiresAt: value + "T23:59" });
}
}}
/>
</div>

View File

@@ -124,7 +124,11 @@ export function UploadFileModal({ isOpen, onClose, onSuccess }: UploadFileModalP
let previewUrl: string | undefined;
if (file.type.startsWith("image/")) {
previewUrl = URL.createObjectURL(file);
try {
previewUrl = URL.createObjectURL(file);
} catch (error) {
console.warn("Failed to create preview URL:", error);
}
}
return {
@@ -142,6 +146,10 @@ export function UploadFileModal({ isOpen, onClose, onSuccess }: UploadFileModalP
const newUploads = Array.from(files).map(createFileUpload);
setFileUploads((prev) => [...prev, ...newUploads]);
setHasShownSuccessToast(false);
if (newUploads.length > 0) {
toast.info(t("uploadFile.filesQueued", { count: newUploads.length }));
}
};
const handleFileInputChange = (event: React.ChangeEvent<HTMLInputElement>) => {
@@ -164,7 +172,12 @@ export function UploadFileModal({ isOpen, onClose, onSuccess }: UploadFileModalP
const handleDrop = (event: React.DragEvent) => {
event.preventDefault();
setIsDragOver(false);
handleFilesSelect(event.dataTransfer.files);
event.stopPropagation();
const files = event.dataTransfer.files;
if (files.length > 0) {
handleFilesSelect(files);
}
};
const renderFileIcon = (fileName: string) => {

View File

@@ -1,6 +1,6 @@
{
"name": "palmr-monorepo",
"version": "3.1.6-beta",
"version": "3.1.7-beta",
"description": "Palmr monorepo with Husky configuration",
"private": true,
"packageManager": "pnpm@10.6.0",