Fügt Unterstützung für Team-Dokumente hinzu. Aktualisiert die Backend-Modelle und -Routen, um Team-Dokumente zu verwalten, einschließlich Upload- und Parsing-Funktionen für Code- und Pin-Listen. Ergänzt die Benutzeroberfläche in TeamManagementView.vue zur Anzeige und Verwaltung von Team-Dokumenten sowie zur Integration von PDF-Parsing. Aktualisiert die Match-Modelle, um zusätzliche Felder für Spiel-Codes und PINs zu berücksichtigen.
This commit is contained in:
@@ -35,6 +35,9 @@
|
||||
<th>Heimmannschaft</th>
|
||||
<th>Gastmannschaft</th>
|
||||
<th v-if="selectedLeague === 'Gesamtspielplan' || selectedLeague === 'Spielplan Erwachsene'">Altersklasse</th>
|
||||
<th>Code</th>
|
||||
<th>Heim-PIN</th>
|
||||
<th>Gast-PIN</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@@ -45,6 +48,18 @@
|
||||
<td v-html="highlightClubName(match.homeTeam?.name || 'N/A')"></td>
|
||||
<td v-html="highlightClubName(match.guestTeam?.name || 'N/A')"></td>
|
||||
<td v-if="selectedLeague === 'Gesamtspielplan' || selectedLeague === 'Spielplan Erwachsene'">{{ match.leagueDetails?.name || 'N/A' }}</td>
|
||||
<td class="code-cell">
|
||||
<span v-if="match.code" class="code-value">{{ match.code }}</span>
|
||||
<span v-else class="no-data">-</span>
|
||||
</td>
|
||||
<td class="pin-cell">
|
||||
<span v-if="match.homePin" class="pin-value">{{ match.homePin }}</span>
|
||||
<span v-else class="no-data">-</span>
|
||||
</td>
|
||||
<td class="pin-cell">
|
||||
<span v-if="match.guestPin" class="pin-value">{{ match.guestPin }}</span>
|
||||
<span v-else class="no-data">-</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -473,6 +488,39 @@ li {
|
||||
color: transparent !important;
|
||||
}
|
||||
|
||||
/* Code und PIN Styles */
|
||||
.code-cell, .pin-cell {
|
||||
text-align: center;
|
||||
font-family: 'Courier New', monospace;
|
||||
font-weight: bold;
|
||||
min-width: 80px;
|
||||
}
|
||||
|
||||
.code-value {
|
||||
background: #e3f2fd;
|
||||
color: #1976d2;
|
||||
padding: 4px 8px;
|
||||
border-radius: 4px;
|
||||
font-size: 0.9rem;
|
||||
display: inline-block;
|
||||
border: 1px solid #bbdefb;
|
||||
}
|
||||
|
||||
.pin-value {
|
||||
background: #fff3e0;
|
||||
color: #f57c00;
|
||||
padding: 4px 8px;
|
||||
border-radius: 4px;
|
||||
font-size: 0.9rem;
|
||||
display: inline-block;
|
||||
border: 1px solid #ffcc02;
|
||||
}
|
||||
|
||||
.no-data {
|
||||
color: #999;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.match-today {
|
||||
background-color: #fff3cd !important; /* Gelb für heute */
|
||||
}
|
||||
|
||||
@@ -50,12 +50,61 @@
|
||||
<h4>Team-Dokumente hochladen</h4>
|
||||
<div class="upload-buttons">
|
||||
<button @click="uploadCodeList" class="upload-btn code-list-btn">
|
||||
📋 Code-Liste hochladen
|
||||
📋 Code-Liste hochladen & parsen
|
||||
</button>
|
||||
<button @click="uploadPinList" class="upload-btn pin-list-btn">
|
||||
🔐 Pin-Liste hochladen
|
||||
🔐 Pin-Liste hochladen & parsen
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Upload-Bestätigung -->
|
||||
<div v-if="showLeagueSelection" class="upload-confirmation">
|
||||
<div class="selected-file-info">
|
||||
<strong>Ausgewählte Datei:</strong> {{ pendingUploadFile?.name }}
|
||||
<br>
|
||||
<strong>Typ:</strong> {{ pendingUploadType === 'code_list' ? 'Code-Liste' : 'Pin-Liste' }}
|
||||
<br>
|
||||
<strong>Team:</strong> {{ teamToEdit?.name }}
|
||||
<br>
|
||||
<strong>Liga:</strong> {{ getTeamLeagueName() }}
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<button
|
||||
@click="confirmUploadAndParse"
|
||||
:disabled="parsingInProgress"
|
||||
class="confirm-parse-btn"
|
||||
>
|
||||
{{ parsingInProgress ? '⏳ Parse läuft...' : '🚀 Hochladen & Parsen' }}
|
||||
</button>
|
||||
<button @click="cancelUpload" class="cancel-parse-btn">
|
||||
Abbrechen
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- PDF-Parsing Bereich für bereits hochgeladene Dokumente -->
|
||||
<div v-if="teamDocuments.length > 0" class="pdf-parsing-section">
|
||||
<h4>Bereits hochgeladene PDF-Dokumente parsen</h4>
|
||||
<div class="document-list">
|
||||
<div v-for="document in teamDocuments" :key="document.id" class="document-item">
|
||||
<div class="document-info">
|
||||
<span class="document-name">{{ document.originalFileName }}</span>
|
||||
<span class="document-type">{{ document.documentType === 'code_list' ? 'Code-Liste' : 'Pin-Liste' }}</span>
|
||||
<span class="document-size">{{ formatFileSize(document.fileSize) }}</span>
|
||||
</div>
|
||||
<div class="document-actions">
|
||||
<button
|
||||
@click="parsePDF(document)"
|
||||
:disabled="document.mimeType !== 'application/pdf'"
|
||||
class="parse-btn"
|
||||
:title="document.mimeType !== 'application/pdf' ? 'Nur PDF-Dateien können geparst werden' : 'PDF parsen und Matches extrahieren'"
|
||||
>
|
||||
🔍 PDF parsen
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -102,6 +151,50 @@
|
||||
<span class="value">{{ formatDate(team.createdAt) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- PDF-Dokumente Icons -->
|
||||
<div class="team-documents">
|
||||
<div class="documents-label">Dokumente:</div>
|
||||
<div class="document-icons">
|
||||
<button
|
||||
v-if="getTeamDocuments(team.id, 'code_list').length > 0"
|
||||
@click.stop="showPDFDialog(team.id, 'code_list')"
|
||||
class="document-icon code-list-icon"
|
||||
title="Code-Liste anzeigen"
|
||||
>
|
||||
📋
|
||||
</button>
|
||||
<button
|
||||
v-if="getTeamDocuments(team.id, 'pin_list').length > 0"
|
||||
@click.stop="showPDFDialog(team.id, 'pin_list')"
|
||||
class="document-icon pin-list-icon"
|
||||
title="Pin-Liste anzeigen"
|
||||
>
|
||||
🔐
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- PDF-Dialog -->
|
||||
<div v-if="showPDFViewer" class="pdf-dialog-overlay" @click="closePDFDialog">
|
||||
<div class="pdf-dialog" @click.stop>
|
||||
<div class="pdf-dialog-header">
|
||||
<h3>{{ pdfDialogTitle }}</h3>
|
||||
<button @click="closePDFDialog" class="close-btn">✕</button>
|
||||
</div>
|
||||
<div class="pdf-dialog-content">
|
||||
<iframe
|
||||
v-if="pdfUrl"
|
||||
:src="pdfUrl"
|
||||
class="pdf-viewer"
|
||||
frameborder="0"
|
||||
></iframe>
|
||||
<div v-else class="no-pdf">
|
||||
<p>PDF konnte nicht geladen werden.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -131,6 +224,16 @@ export default {
|
||||
const newLeagueId = ref('');
|
||||
const selectedSeasonId = ref(null);
|
||||
const currentSeason = ref(null);
|
||||
const teamDocuments = ref([]);
|
||||
const pendingUploadFile = ref(null);
|
||||
const pendingUploadType = ref(null);
|
||||
const showLeagueSelection = ref(false);
|
||||
const parsingInProgress = ref(false);
|
||||
|
||||
// PDF-Dialog Variablen
|
||||
const showPDFViewer = ref(false);
|
||||
const pdfUrl = ref('');
|
||||
const pdfDialogTitle = ref('');
|
||||
|
||||
// Computed
|
||||
const selectedClub = computed(() => store.state.currentClub);
|
||||
@@ -169,6 +272,9 @@ export default {
|
||||
const response = await apiClient.get(`/club-teams/club/${selectedClub.value}?seasonid=${selectedSeasonId.value}`);
|
||||
console.log('TeamManagementView: Loaded club teams:', response.data);
|
||||
teams.value = response.data;
|
||||
|
||||
// Lade alle Team-Dokumente nach dem Laden der Teams
|
||||
await loadAllTeamDocuments();
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Laden der Club-Teams:', error);
|
||||
}
|
||||
@@ -228,6 +334,7 @@ export default {
|
||||
newTeamName.value = team.name;
|
||||
newLeagueId.value = team.leagueId || '';
|
||||
teamFormIsOpen.value = true;
|
||||
loadTeamDocuments();
|
||||
};
|
||||
|
||||
const deleteTeam = async (team) => {
|
||||
@@ -254,14 +361,12 @@ export default {
|
||||
const file = event.target.files[0];
|
||||
if (!file) return;
|
||||
|
||||
try {
|
||||
console.log('Code-Liste hochladen für Team:', teamToEdit.value.name, 'Datei:', file.name);
|
||||
// TODO: Implementiere Upload-Logik für Code-Liste
|
||||
alert(`Code-Liste "${file.name}" würde für Team "${teamToEdit.value.name}" hochgeladen werden.`);
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Hochladen der Code-Liste:', error);
|
||||
alert('Fehler beim Hochladen der Code-Liste');
|
||||
}
|
||||
// Speichere die Datei und den Typ für späteres Parsing
|
||||
pendingUploadFile.value = file;
|
||||
pendingUploadType.value = 'code_list';
|
||||
|
||||
// Zeige Liga-Auswahl für Parsing
|
||||
showLeagueSelection.value = true;
|
||||
};
|
||||
input.click();
|
||||
};
|
||||
@@ -277,14 +382,12 @@ export default {
|
||||
const file = event.target.files[0];
|
||||
if (!file) return;
|
||||
|
||||
try {
|
||||
console.log('Pin-Liste hochladen für Team:', teamToEdit.value.name, 'Datei:', file.name);
|
||||
// TODO: Implementiere Upload-Logik für Pin-Liste
|
||||
alert(`Pin-Liste "${file.name}" würde für Team "${teamToEdit.value.name}" hochgeladen werden.`);
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Hochladen der Pin-Liste:', error);
|
||||
alert('Fehler beim Hochladen der Pin-Liste');
|
||||
}
|
||||
// Speichere die Datei und den Typ für späteres Parsing
|
||||
pendingUploadFile.value = file;
|
||||
pendingUploadType.value = 'pin_list';
|
||||
|
||||
// Zeige Liga-Auswahl für Parsing
|
||||
showLeagueSelection.value = true;
|
||||
};
|
||||
input.click();
|
||||
};
|
||||
@@ -292,6 +395,181 @@ export default {
|
||||
const formatDate = (dateString) => {
|
||||
return new Date(dateString).toLocaleDateString('de-DE');
|
||||
};
|
||||
|
||||
const formatFileSize = (bytes) => {
|
||||
if (bytes === 0) return '0 Bytes';
|
||||
const k = 1024;
|
||||
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
|
||||
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
||||
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
|
||||
};
|
||||
|
||||
const loadTeamDocuments = async () => {
|
||||
if (!teamToEdit.value) return;
|
||||
|
||||
try {
|
||||
console.log('TeamManagementView: Loading documents for team:', teamToEdit.value.id);
|
||||
const response = await apiClient.get(`/team-documents/club-team/${teamToEdit.value.id}`);
|
||||
console.log('TeamManagementView: Loaded documents:', response.data);
|
||||
teamDocuments.value = response.data;
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Laden der Team-Dokumente:', error);
|
||||
}
|
||||
};
|
||||
|
||||
const loadAllTeamDocuments = async () => {
|
||||
if (!selectedClub.value) return;
|
||||
|
||||
try {
|
||||
console.log('TeamManagementView: Loading all team documents for club:', selectedClub.value);
|
||||
// Lade alle Dokumente für alle Teams des Clubs
|
||||
const allDocuments = [];
|
||||
for (const team of teams.value) {
|
||||
try {
|
||||
const response = await apiClient.get(`/team-documents/club-team/${team.id}`);
|
||||
allDocuments.push(...response.data);
|
||||
} catch (error) {
|
||||
console.warn(`Fehler beim Laden der Dokumente für Team ${team.id}:`, error);
|
||||
}
|
||||
}
|
||||
teamDocuments.value = allDocuments;
|
||||
console.log('TeamManagementView: Loaded all team documents:', allDocuments.length);
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Laden aller Team-Dokumente:', error);
|
||||
}
|
||||
};
|
||||
|
||||
const confirmUploadAndParse = async () => {
|
||||
if (!pendingUploadFile.value || !teamToEdit.value?.leagueId) {
|
||||
alert('Team ist keiner Liga zugeordnet!');
|
||||
return;
|
||||
}
|
||||
|
||||
parsingInProgress.value = true;
|
||||
|
||||
try {
|
||||
console.log('Datei hochladen und parsen:', pendingUploadFile.value.name, 'Typ:', pendingUploadType.value, 'für Liga:', teamToEdit.value.leagueId);
|
||||
|
||||
// Schritt 1: Datei als Team-Dokument hochladen
|
||||
const formData = new FormData();
|
||||
formData.append('document', pendingUploadFile.value);
|
||||
formData.append('documentType', pendingUploadType.value);
|
||||
|
||||
const uploadResponse = await apiClient.post(`/team-documents/club-team/${teamToEdit.value.id}/upload`, formData, {
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data'
|
||||
}
|
||||
});
|
||||
|
||||
console.log('Datei hochgeladen:', uploadResponse.data);
|
||||
|
||||
// Schritt 2: Datei parsen (nur für PDF/TXT-Dateien)
|
||||
const fileExtension = pendingUploadFile.value.name.toLowerCase().split('.').pop();
|
||||
if (fileExtension === 'pdf' || fileExtension === 'txt') {
|
||||
const parseResponse = await apiClient.post(`/team-documents/${uploadResponse.data.id}/parse?leagueid=${teamToEdit.value.leagueId}`);
|
||||
|
||||
console.log('Datei erfolgreich geparst:', parseResponse.data);
|
||||
|
||||
const { parseResult, saveResult } = parseResponse.data;
|
||||
|
||||
let message = `${pendingUploadType.value === 'code_list' ? 'Code-Liste' : 'Pin-Liste'} erfolgreich hochgeladen und geparst!\n\n`;
|
||||
message += `Gefundene Spiele: ${parseResult.matchesFound}\n`;
|
||||
message += `Neue Spiele erstellt: ${saveResult.created}\n`;
|
||||
message += `Spiele aktualisiert: ${saveResult.updated}\n`;
|
||||
|
||||
if (saveResult.errors.length > 0) {
|
||||
message += `\nFehler: ${saveResult.errors.length}\n`;
|
||||
message += saveResult.errors.slice(0, 3).join('\n');
|
||||
if (saveResult.errors.length > 3) {
|
||||
message += `\n... und ${saveResult.errors.length - 3} weitere`;
|
||||
}
|
||||
}
|
||||
|
||||
// Debug-Informationen anzeigen wenn keine Matches gefunden wurden
|
||||
if (parseResult.matchesFound === 0) {
|
||||
message += `\n\n--- DEBUG-INFORMATIONEN ---\n`;
|
||||
message += `Text-Länge: ${parseResult.debugInfo.totalTextLength} Zeichen\n`;
|
||||
message += `Zeilen: ${parseResult.debugInfo.totalLines}\n`;
|
||||
message += `Erste Zeilen:\n`;
|
||||
parseResult.debugInfo.firstFewLines.forEach((line, index) => {
|
||||
message += `${index + 1}: "${line}"\n`;
|
||||
});
|
||||
message += `\nLetzte Zeilen:\n`;
|
||||
parseResult.debugInfo.lastFewLines.forEach((line, index) => {
|
||||
message += `${parseResult.debugInfo.totalLines - 5 + index + 1}: "${line}"\n`;
|
||||
});
|
||||
}
|
||||
|
||||
alert(message);
|
||||
} else {
|
||||
// Für andere Dateitypen nur Upload-Bestätigung
|
||||
alert(`${pendingUploadType.value === 'code_list' ? 'Code-Liste' : 'Pin-Liste'} "${pendingUploadFile.value.name}" wurde erfolgreich hochgeladen!`);
|
||||
}
|
||||
|
||||
// Dokumente neu laden
|
||||
await loadTeamDocuments();
|
||||
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Hochladen und Parsen der Datei:', error);
|
||||
alert('Fehler beim Hochladen und Parsen der Datei');
|
||||
} finally {
|
||||
parsingInProgress.value = false;
|
||||
pendingUploadFile.value = null;
|
||||
pendingUploadType.value = null;
|
||||
showLeagueSelection.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const cancelUpload = () => {
|
||||
pendingUploadFile.value = null;
|
||||
pendingUploadType.value = null;
|
||||
showLeagueSelection.value = false;
|
||||
};
|
||||
|
||||
const getTeamLeagueName = () => {
|
||||
if (!teamToEdit.value?.leagueId) return 'Keine Liga zugeordnet';
|
||||
const league = leagues.value.find(l => l.id === teamToEdit.value.leagueId);
|
||||
return league ? league.name : 'Unbekannte Liga';
|
||||
};
|
||||
|
||||
|
||||
|
||||
const parsePDF = async (document) => {
|
||||
// Finde das Team für dieses Dokument
|
||||
const team = teams.value.find(t => t.id === document.clubTeamId);
|
||||
if (!team || !team.leagueId) {
|
||||
alert('Team ist keiner Liga zugeordnet!');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
console.log('PDF parsen:', document.originalFileName, 'für Liga:', team.leagueId);
|
||||
|
||||
const response = await apiClient.post(`/team-documents/${document.id}/parse?leagueid=${team.leagueId}`);
|
||||
|
||||
console.log('PDF erfolgreich geparst:', response.data);
|
||||
|
||||
const { parseResult, saveResult } = response.data;
|
||||
|
||||
let message = `PDF erfolgreich geparst!\n\n`;
|
||||
message += `Gefundene Spiele: ${parseResult.matchesFound}\n`;
|
||||
message += `Neue Spiele erstellt: ${saveResult.created}\n`;
|
||||
message += `Spiele aktualisiert: ${saveResult.updated}\n`;
|
||||
|
||||
if (saveResult.errors.length > 0) {
|
||||
message += `\nFehler: ${saveResult.errors.length}\n`;
|
||||
message += saveResult.errors.slice(0, 3).join('\n');
|
||||
if (saveResult.errors.length > 3) {
|
||||
message += `\n... und ${saveResult.errors.length - 3} weitere`;
|
||||
}
|
||||
}
|
||||
|
||||
alert(message);
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Parsen der PDF:', error);
|
||||
alert('Fehler beim Parsen der PDF-Datei');
|
||||
}
|
||||
};
|
||||
|
||||
const onSeasonChange = (season) => {
|
||||
currentSeason.value = season;
|
||||
@@ -310,6 +588,46 @@ export default {
|
||||
loadLeagues();
|
||||
});
|
||||
|
||||
// PDF-Dialog Funktionen
|
||||
const getTeamDocuments = (teamId, documentType) => {
|
||||
return teamDocuments.value.filter(doc =>
|
||||
doc.clubTeamId === teamId && doc.documentType === documentType
|
||||
);
|
||||
};
|
||||
|
||||
const showPDFDialog = async (teamId, documentType) => {
|
||||
const documents = getTeamDocuments(teamId, documentType);
|
||||
if (documents.length === 0) return;
|
||||
|
||||
const document = documents[0]; // Nehme das erste Dokument
|
||||
const team = teams.value.find(t => t.id === teamId);
|
||||
|
||||
pdfDialogTitle.value = `${team?.name || 'Team'} - ${documentType === 'code_list' ? 'Code-Liste' : 'Pin-Liste'}`;
|
||||
|
||||
try {
|
||||
// Lade das PDF über die API
|
||||
const response = await apiClient.get(`/team-documents/${document.id}/download`, {
|
||||
responseType: 'blob'
|
||||
});
|
||||
|
||||
// Erstelle eine URL für das PDF
|
||||
const blob = new Blob([response.data], { type: 'application/pdf' });
|
||||
pdfUrl.value = URL.createObjectURL(blob);
|
||||
showPDFViewer.value = true;
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Laden des PDFs:', error);
|
||||
alert('Fehler beim Laden des PDFs');
|
||||
}
|
||||
};
|
||||
|
||||
const closePDFDialog = () => {
|
||||
showPDFViewer.value = false;
|
||||
if (pdfUrl.value) {
|
||||
URL.revokeObjectURL(pdfUrl.value);
|
||||
pdfUrl.value = '';
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
teams,
|
||||
leagues,
|
||||
@@ -319,6 +637,14 @@ export default {
|
||||
newLeagueId,
|
||||
selectedSeasonId,
|
||||
currentSeason,
|
||||
teamDocuments,
|
||||
pendingUploadFile,
|
||||
pendingUploadType,
|
||||
showLeagueSelection,
|
||||
parsingInProgress,
|
||||
showPDFViewer,
|
||||
pdfUrl,
|
||||
pdfDialogTitle,
|
||||
filteredLeagues,
|
||||
toggleNewTeam,
|
||||
resetToNewTeam,
|
||||
@@ -328,7 +654,17 @@ export default {
|
||||
deleteTeam,
|
||||
uploadCodeList,
|
||||
uploadPinList,
|
||||
loadTeamDocuments,
|
||||
loadAllTeamDocuments,
|
||||
confirmUploadAndParse,
|
||||
cancelUpload,
|
||||
getTeamLeagueName,
|
||||
parsePDF,
|
||||
getTeamDocuments,
|
||||
showPDFDialog,
|
||||
closePDFDialog,
|
||||
formatDate,
|
||||
formatFileSize,
|
||||
onSeasonChange
|
||||
};
|
||||
}
|
||||
@@ -609,4 +945,326 @@ export default {
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
|
||||
/* Upload-Bestätigung */
|
||||
.upload-confirmation {
|
||||
margin-top: 1.5rem;
|
||||
padding: 1.5rem;
|
||||
background: #f8f9fa;
|
||||
border-radius: var(--border-radius-medium);
|
||||
border: 2px solid #dee2e6;
|
||||
}
|
||||
|
||||
.selected-file-info {
|
||||
background: #e9ecef;
|
||||
padding: 1rem;
|
||||
border-radius: var(--border-radius-small);
|
||||
margin-bottom: 1rem;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
|
||||
.action-buttons {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
|
||||
.confirm-parse-btn {
|
||||
background: #4caf50;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 0.75rem 1.5rem;
|
||||
border-radius: var(--border-radius-small);
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: var(--transition);
|
||||
margin-right: 1rem;
|
||||
}
|
||||
|
||||
.confirm-parse-btn:hover:not(:disabled) {
|
||||
background: #45a049;
|
||||
}
|
||||
|
||||
.confirm-parse-btn:disabled {
|
||||
background: #cccccc;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.cancel-parse-btn {
|
||||
background: #6c757d;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 0.75rem 1.5rem;
|
||||
border-radius: var(--border-radius-small);
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: var(--transition);
|
||||
}
|
||||
|
||||
.cancel-parse-btn:hover {
|
||||
background: #5a6268;
|
||||
}
|
||||
|
||||
.selected-file {
|
||||
display: block;
|
||||
margin-top: 0.5rem;
|
||||
font-size: 0.875rem;
|
||||
color: #2e7d32;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.parse-options {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
align-items: end;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.parse-options label {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
min-width: 200px;
|
||||
}
|
||||
|
||||
.parse-options label span {
|
||||
font-weight: 600;
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
/* PDF-Parsing Styles */
|
||||
.pdf-parsing-section {
|
||||
margin-top: 2rem;
|
||||
padding: 1.5rem;
|
||||
background: var(--background-light);
|
||||
border-radius: var(--border-radius-medium);
|
||||
border: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.pdf-parsing-section h4 {
|
||||
margin: 0 0 1rem 0;
|
||||
color: var(--text-color);
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.document-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.document-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 1rem;
|
||||
background: white;
|
||||
border-radius: var(--border-radius-small);
|
||||
border: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.document-info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.25rem;
|
||||
}
|
||||
|
||||
.document-name {
|
||||
font-weight: 600;
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
.document-type {
|
||||
font-size: 0.875rem;
|
||||
color: var(--text-muted);
|
||||
background: var(--primary-light);
|
||||
padding: 0.25rem 0.5rem;
|
||||
border-radius: var(--border-radius-small);
|
||||
display: inline-block;
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
.document-size {
|
||||
font-size: 0.75rem;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.document-actions {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.parse-btn {
|
||||
padding: 0.5rem 1rem;
|
||||
background: #2196F3;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: var(--border-radius-small);
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: var(--transition);
|
||||
}
|
||||
|
||||
.parse-btn:hover:not(:disabled) {
|
||||
background: #1976D2;
|
||||
}
|
||||
|
||||
.parse-btn:disabled {
|
||||
background: var(--text-muted);
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
/* Team-Dokumente Styles */
|
||||
.team-documents {
|
||||
margin-top: 1rem;
|
||||
padding-top: 1rem;
|
||||
border-top: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.documents-label {
|
||||
font-size: 0.875rem;
|
||||
font-weight: 600;
|
||||
color: var(--text-muted);
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.document-icons {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.document-icon {
|
||||
background: none;
|
||||
border: 2px solid var(--border-color);
|
||||
border-radius: var(--border-radius-small);
|
||||
padding: 0.5rem;
|
||||
cursor: pointer;
|
||||
font-size: 1.25rem;
|
||||
transition: var(--transition);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-width: 2.5rem;
|
||||
height: 2.5rem;
|
||||
}
|
||||
|
||||
.document-icon:hover {
|
||||
border-color: var(--primary-color);
|
||||
background: var(--primary-color);
|
||||
color: white;
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.code-list-icon:hover {
|
||||
border-color: #4CAF50;
|
||||
background: #4CAF50;
|
||||
}
|
||||
|
||||
.pin-list-icon:hover {
|
||||
border-color: #FF9800;
|
||||
background: #FF9800;
|
||||
}
|
||||
|
||||
/* PDF-Dialog Styles */
|
||||
.pdf-dialog-overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgba(0, 0, 0, 0.8);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.pdf-dialog {
|
||||
background: white;
|
||||
border-radius: var(--border-radius-medium);
|
||||
width: 90%;
|
||||
height: 90%;
|
||||
max-width: 1200px;
|
||||
max-height: 800px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.pdf-dialog-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 1rem 1.5rem;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
background: var(--background-light);
|
||||
border-radius: var(--border-radius-medium) var(--border-radius-medium) 0 0;
|
||||
}
|
||||
|
||||
.pdf-dialog-header h3 {
|
||||
margin: 0;
|
||||
color: var(--text-primary);
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
|
||||
.close-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
font-size: 1.5rem;
|
||||
cursor: pointer;
|
||||
color: var(--text-muted);
|
||||
padding: 0.25rem;
|
||||
border-radius: var(--border-radius-small);
|
||||
transition: var(--transition);
|
||||
}
|
||||
|
||||
.close-btn:hover {
|
||||
background: var(--background-light);
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.pdf-dialog-content {
|
||||
flex: 1;
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.pdf-viewer {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.no-pdf {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.document-item {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.document-actions {
|
||||
width: 100%;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.parse-options {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.parse-options label {
|
||||
min-width: auto;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user