mirror of
https://github.com/Dvorinka/PPve.git
synced 2026-06-04 20:42:59 +00:00
Add files via upload
This commit is contained in:
+34
-20
@@ -23,9 +23,10 @@ type Contact struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type ContactData struct {
|
type ContactData struct {
|
||||||
Contacts []Contact `json:"contacts"`
|
Contacts []Contact `json:"contacts"`
|
||||||
LastUpdated time.Time `json:"last_updated"`
|
InternalContacts []Contact `json:"internal_contacts"`
|
||||||
FileHash string `json:"file_hash"`
|
LastUpdated time.Time `json:"last_updated"`
|
||||||
|
FileHash string `json:"file_hash"`
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -84,9 +85,10 @@ func loadData() {
|
|||||||
if _, err := os.Stat(xlsxFile); os.IsNotExist(err) {
|
if _, err := os.Stat(xlsxFile); os.IsNotExist(err) {
|
||||||
log.Printf("Excel file %s not found, using empty data", xlsxFile)
|
log.Printf("Excel file %s not found, using empty data", xlsxFile)
|
||||||
currentData = &ContactData{
|
currentData = &ContactData{
|
||||||
Contacts: []Contact{},
|
Contacts: []Contact{},
|
||||||
LastUpdated: time.Now(),
|
InternalContacts: []Contact{},
|
||||||
FileHash: "",
|
LastUpdated: time.Now(),
|
||||||
|
FileHash: "",
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -114,25 +116,23 @@ func loadData() {
|
|||||||
log.Printf("Error parsing Excel file: %v", err)
|
log.Printf("Error parsing Excel file: %v", err)
|
||||||
// Use empty data if parsing fails
|
// Use empty data if parsing fails
|
||||||
currentData = &ContactData{
|
currentData = &ContactData{
|
||||||
Contacts: []Contact{},
|
Contacts: []Contact{},
|
||||||
LastUpdated: time.Now(),
|
InternalContacts: []Contact{},
|
||||||
FileHash: currentHash,
|
LastUpdated: time.Now(),
|
||||||
|
FileHash: currentHash,
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
currentData = &ContactData{
|
currentData = processContacts(contacts)
|
||||||
Contacts: contacts,
|
currentData.FileHash = currentHash
|
||||||
LastUpdated: time.Now(),
|
|
||||||
FileHash: currentHash,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save to cache
|
// Save to cache
|
||||||
if err := saveCachedData(currentData); err != nil {
|
if err := saveCachedData(currentData); err != nil {
|
||||||
log.Printf("Warning: Could not save cached data: %v", err)
|
log.Printf("Warning: Could not save cached data: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("Loaded %d contacts from Excel file", len(contacts))
|
log.Printf("Loaded %d contacts from Excel file", len(currentData.Contacts) + len(currentData.InternalContacts))
|
||||||
}
|
}
|
||||||
|
|
||||||
func calculateFileHash(filename string) (string, error) {
|
func calculateFileHash(filename string) (string, error) {
|
||||||
@@ -214,10 +214,11 @@ func parseTable(f *excelize.File, sheetName, startCol, endCol string, tableNum i
|
|||||||
return contacts
|
return contacts
|
||||||
}
|
}
|
||||||
|
|
||||||
// Skip header rows (first 3 rows based on your description)
|
// Skip first 3 and last 3 lines
|
||||||
startRow := 3
|
startRow := 3
|
||||||
if len(rows) <= startRow {
|
endRow := len(rows) - 3
|
||||||
return contacts
|
if endRow <= startRow {
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Column indices
|
// Column indices
|
||||||
@@ -228,7 +229,7 @@ func parseTable(f *excelize.File, sheetName, startCol, endCol string, tableNum i
|
|||||||
mobileKlapkaCol = 4
|
mobileKlapkaCol = 4
|
||||||
)
|
)
|
||||||
|
|
||||||
for i := startRow; i < len(rows); i++ {
|
for i := startRow; i < endRow; i++ {
|
||||||
row := rows[i]
|
row := rows[i]
|
||||||
|
|
||||||
// Skip if row is too short
|
// Skip if row is too short
|
||||||
@@ -303,6 +304,19 @@ func cleanPhoneNumber(phone string) string {
|
|||||||
return phone
|
return phone
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func processContacts(contacts []Contact) *ContactData {
|
||||||
|
var data ContactData
|
||||||
|
for _, contact := range contacts {
|
||||||
|
if strings.Contains(contact.Name, "Interní") {
|
||||||
|
data.InternalContacts = append(data.InternalContacts, contact)
|
||||||
|
} else {
|
||||||
|
data.Contacts = append(data.Contacts, contact)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
data.LastUpdated = time.Now()
|
||||||
|
return &data
|
||||||
|
}
|
||||||
|
|
||||||
func serveIndex(w http.ResponseWriter, r *http.Request) {
|
func serveIndex(w http.ResponseWriter, r *http.Request) {
|
||||||
if r.URL.Path != "/" {
|
if r.URL.Path != "/" {
|
||||||
http.NotFound(w, r)
|
http.NotFound(w, r)
|
||||||
@@ -344,7 +358,7 @@ func reloadData(w http.ResponseWriter, r *http.Request) {
|
|||||||
loadData()
|
loadData()
|
||||||
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
fmt.Fprintf(w, `{"status": "reloaded", "contacts_count": %d}`, len(currentData.Contacts))
|
fmt.Fprintf(w, `{"status": "reloaded", "contacts_count": %d}`, len(currentData.Contacts) + len(currentData.InternalContacts))
|
||||||
}
|
}
|
||||||
|
|
||||||
func getEmbeddedHTML() string {
|
func getEmbeddedHTML() string {
|
||||||
|
|||||||
+64
-49
@@ -80,7 +80,19 @@
|
|||||||
<div id="stats" class="mb-6 p-4 bg-gray-50 rounded-lg border text-sm text-gray-700"></div>
|
<div id="stats" class="mb-6 p-4 bg-gray-50 rounded-lg border text-sm text-gray-700"></div>
|
||||||
|
|
||||||
<!-- Contacts Grid -->
|
<!-- Contacts Grid -->
|
||||||
<div id="contacts" class="grid gap-6 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4"></div>
|
<div class="container">
|
||||||
|
<h1>Kontakty</h1>
|
||||||
|
|
||||||
|
<div class="section">
|
||||||
|
<h2>Hlavní kontakty</h2>
|
||||||
|
<div id="contacts" class="grid gap-6 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="section">
|
||||||
|
<h2>Interní kontakty</h2>
|
||||||
|
<div id="internal-contacts" class="grid gap-6 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- No Results -->
|
<!-- No Results -->
|
||||||
<div id="noResults" class="hidden text-center py-16">
|
<div id="noResults" class="hidden text-center py-16">
|
||||||
@@ -228,57 +240,60 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function displayContacts(contacts, searchQuery = '') {
|
function displayContacts(contacts, searchQuery = '') {
|
||||||
const container = document.getElementById('contacts');
|
const mainContacts = contacts.filter(contact => !contact.internal);
|
||||||
const noResults = document.getElementById('noResults');
|
const internalContacts = contacts.filter(contact => contact.internal);
|
||||||
|
|
||||||
if (contacts.length === 0) {
|
const mainContactsDiv = document.getElementById('contacts');
|
||||||
container.innerHTML = '';
|
const internalContactsDiv = document.getElementById('internal-contacts');
|
||||||
noResults.classList.remove('hidden');
|
|
||||||
return;
|
mainContactsDiv.innerHTML = mainContacts.map(contact =>
|
||||||
|
formatContactCard(contact, searchQuery)
|
||||||
|
).join('');
|
||||||
|
|
||||||
|
internalContactsDiv.innerHTML = internalContacts.map(contact =>
|
||||||
|
formatContactCard(contact, searchQuery)
|
||||||
|
).join('');
|
||||||
|
|
||||||
|
if (mainContacts.length === 0 && internalContacts.length === 0) {
|
||||||
|
document.getElementById('noResults').classList.remove('hidden');
|
||||||
|
} else {
|
||||||
|
document.getElementById('noResults').classList.add('hidden');
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatContactCard(contact, searchQuery) {
|
||||||
|
const name = contact.name || 'Bez jména';
|
||||||
|
const position = contact.position || '';
|
||||||
|
const tableColor = contact.table === 1 ? 'bg-blue-100 text-blue-800' : 'bg-purple-100 text-purple-800';
|
||||||
|
|
||||||
noResults.classList.add('hidden');
|
return `<div class="contact-card bg-white p-6 rounded-lg border border-gray-200 hover:shadow-lg transition-all duration-200">
|
||||||
container.innerHTML = '';
|
<div class="flex items-start justify-between mb-3">
|
||||||
|
<h3 class="font-bold text-gray-800 text-lg leading-tight">${highlightText(name, searchQuery)}</h3>
|
||||||
contacts.forEach(contact => {
|
<span class="text-xs ${tableColor} px-2 py-1 rounded-full font-medium flex-shrink-0 ml-2">T${contact.table}</span>
|
||||||
const contactCard = document.createElement('div');
|
</div>
|
||||||
contactCard.className = 'contact-card bg-white p-6 rounded-lg border border-gray-200 hover:shadow-lg transition-all duration-200';
|
${position ? `<p class="text-gray-600 mb-4 text-sm leading-relaxed">${highlightText(position, searchQuery)}</p>` : ''}
|
||||||
|
<div class="space-y-2">
|
||||||
const name = contact.name || 'Bez jména';
|
${contact.phone ? `
|
||||||
const position = contact.position || '';
|
<div class="flex items-center text-sm group">
|
||||||
const tableColor = contact.table === 1 ? 'bg-blue-100 text-blue-800' : 'bg-purple-100 text-purple-800';
|
<i class="fas fa-phone-alt text-gray-400 mr-3 flex-shrink-0"></i>
|
||||||
|
<span class="font-medium text-gray-700 mr-2">Tel:</span>
|
||||||
contactCard.innerHTML = `
|
<a href="tel:${contact.phone}" class="text-blue-600 hover:text-blue-800 hover:underline transition-colors">
|
||||||
<div class="flex items-start justify-between mb-3">
|
${highlightText(contact.phone, searchQuery)}
|
||||||
<h3 class="font-bold text-gray-800 text-lg leading-tight">${highlightText(name, searchQuery)}</h3>
|
</a>
|
||||||
<span class="text-xs ${tableColor} px-2 py-1 rounded-full font-medium flex-shrink-0 ml-2">T${contact.table}</span>
|
</div>
|
||||||
</div>
|
` : ''}
|
||||||
${position ? `<p class="text-gray-600 mb-4 text-sm leading-relaxed">${highlightText(position, searchQuery)}</p>` : ''}
|
${contact.service_phone ? `
|
||||||
<div class="space-y-2">
|
<div class="flex items-center text-sm group">
|
||||||
${contact.phone ? `
|
<i class="fas fa-phone-alt text-gray-400 mr-3 flex-shrink-0"></i>
|
||||||
<div class="flex items-center text-sm group">
|
<span class="font-medium text-gray-700 mr-2">Služební:</span>
|
||||||
<i class="fas fa-phone-alt text-gray-400 mr-3 flex-shrink-0"></i>
|
<a href="tel:${contact.service_phone}" class="text-blue-600 hover:text-blue-800 hover:underline transition-colors">
|
||||||
<span class="font-medium text-gray-700 mr-2">Tel:</span>
|
${highlightText(contact.service_phone, searchQuery)}
|
||||||
<a href="tel:${contact.phone}" class="text-blue-600 hover:text-blue-800 hover:underline transition-colors">
|
</a>
|
||||||
${highlightText(contact.phone, searchQuery)}
|
</div>
|
||||||
</a>
|
` : ''}
|
||||||
</div>
|
${!contact.phone && !contact.service_phone ? '<p class="text-gray-400 text-sm italic">Bez telefonu</p>' : ''}
|
||||||
` : ''}
|
</div>
|
||||||
${contact.service_phone ? `
|
</div>`;
|
||||||
<div class="flex items-center text-sm group">
|
|
||||||
<i class="fas fa-phone-alt text-gray-400 mr-3 flex-shrink-0"></i>
|
|
||||||
<span class="font-medium text-gray-700 mr-2">Služební:</span>
|
|
||||||
<a href="tel:${contact.service_phone}" class="text-blue-600 hover:text-blue-800 hover:underline transition-colors">
|
|
||||||
${highlightText(contact.service_phone, searchQuery)}
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
` : ''}
|
|
||||||
${!contact.phone && !contact.service_phone ? '<p class="text-gray-400 text-sm italic">Bez telefonu</p>' : ''}
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
|
|
||||||
container.appendChild(contactCard);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function reloadContacts() {
|
async function reloadContacts() {
|
||||||
|
|||||||
Reference in New Issue
Block a user