diff --git a/resources/js/Layouts/AdminLayout.vue b/resources/js/Layouts/AdminLayout.vue
index 544493d..9d42617 100644
--- a/resources/js/Layouts/AdminLayout.vue
+++ b/resources/js/Layouts/AdminLayout.vue
@@ -11,6 +11,10 @@ import {
faGears,
faKey,
faEnvelope,
+ faEnvelopeOpenText,
+ faAt,
+ faInbox,
+ faFileLines,
} from "@fortawesome/free-solid-svg-icons";
import Dropdown from "@/Components/Dropdown.vue";
import DropdownLink from "@/Components/DropdownLink.vue";
@@ -44,7 +48,7 @@ function toggleSidebar() {
const logout = () => router.post(route("logout"));
const page = usePage();
-// Categorized admin navigation groups (removed global 'Nastavitve')
+// Categorized admin navigation groups with distinct icons
const navGroups = computed(() => [
{
key: "core",
@@ -97,29 +101,35 @@ const navGroups = computed(() => [
icon: faFileWord,
active: ["admin.document-templates.index"],
},
- {
- key: "admin.email-templates.index",
- label: "Email predloge",
- route: "admin.email-templates.index",
- icon: faEnvelope,
- active: [
- "admin.email-templates.index",
- "admin.email-templates.create",
- "admin.email-templates.edit",
- ],
- },
+ ],
+ },
+ {
+ key: "email",
+ label: "Email",
+ items: [
+ {
+ key: "admin.email-templates.index",
+ label: "Email predloge",
+ route: "admin.email-templates.index",
+ icon: faEnvelopeOpenText,
+ active: [
+ "admin.email-templates.index",
+ "admin.email-templates.create",
+ "admin.email-templates.edit",
+ ],
+ },
{
key: "admin.email-logs.index",
label: "Email dnevniki",
route: "admin.email-logs.index",
- icon: faEnvelope,
+ icon: faInbox,
active: ["admin.email-logs.index", "admin.email-logs.show"],
},
{
key: "admin.mail-profiles.index",
label: "Mail profili",
route: "admin.mail-profiles.index",
- icon: faGears,
+ icon: faAt,
active: ["admin.mail-profiles.index"],
},
],
diff --git a/resources/js/Pages/Admin/Index.vue b/resources/js/Pages/Admin/Index.vue
index 0aaa95d..34558ff 100644
--- a/resources/js/Pages/Admin/Index.vue
+++ b/resources/js/Pages/Admin/Index.vue
@@ -2,7 +2,7 @@
import AdminLayout from '@/Layouts/AdminLayout.vue'
import { Link } from '@inertiajs/vue3'
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'
-import { faUserGroup, faKey, faGears, faFileWord } from '@fortawesome/free-solid-svg-icons'
+import { faUserGroup, faKey, faGears, faFileWord, faEnvelopeOpenText, faInbox, faAt } from '@fortawesome/free-solid-svg-icons'
const cards = [
{
@@ -39,6 +39,29 @@ const cards = [
},
],
},
+ {
+ category: 'Email',
+ items: [
+ {
+ title: 'Email predloge',
+ description: 'Upravljanje HTML / tekst email predlog',
+ route: 'admin.email-templates.index',
+ icon: faEnvelopeOpenText,
+ },
+ {
+ title: 'Email dnevniki',
+ description: 'Pregled poslanih emailov in statusov',
+ route: 'admin.email-logs.index',
+ icon: faInbox,
+ },
+ {
+ title: 'Mail profili',
+ description: 'SMTP profili, nastavitve in testiranje povezave',
+ route: 'admin.mail-profiles.index',
+ icon: faAt,
+ },
+ ],
+ },
]
diff --git a/resources/js/Pages/Admin/MailProfiles/Index.vue b/resources/js/Pages/Admin/MailProfiles/Index.vue
index 077f211..e9cd7b2 100644
--- a/resources/js/Pages/Admin/MailProfiles/Index.vue
+++ b/resources/js/Pages/Admin/MailProfiles/Index.vue
@@ -18,8 +18,9 @@ const props = defineProps({
profiles: { type: Array, default: () => [] },
});
-const createOpen = ref(false);
-const editTarget = ref(null);
+const createOpen = ref(false); // create modal
+const editOpen = ref(false); // edit modal
+const editTarget = ref(null); // profile being edited
const form = useForm({
name: "",
@@ -39,6 +40,22 @@ function openCreate() {
editTarget.value = null;
}
+function openEdit(p) {
+ // populate form with existing profile data (exclude password which is write-only)
+ form.reset();
+ form.name = p.name || "";
+ form.host = p.host || "";
+ form.port = p.port || 587;
+ form.encryption = p.encryption || "";
+ form.username = p.username || "";
+ form.password = ""; // empty -> keep existing unless user fills
+ form.from_address = p.from_address || "";
+ form.from_name = p.from_name || "";
+ form.priority = p.priority ?? 10;
+ editTarget.value = p;
+ editOpen.value = true;
+}
+
function closeCreate() {
if (form.processing) return;
createOpen.value = false;
@@ -53,6 +70,37 @@ function submitCreate() {
});
}
+function closeEdit() {
+ if (form.processing) return;
+ editOpen.value = false;
+ editTarget.value = null;
+}
+
+function submitEdit() {
+ if (!editTarget.value) return;
+ // Build payload excluding empty password
+ const payload = {
+ name: form.name,
+ host: form.host,
+ port: form.port,
+ encryption: form.encryption || null,
+ username: form.username || null,
+ from_address: form.from_address,
+ from_name: form.from_name || null,
+ priority: form.priority,
+ };
+ if (form.password && form.password.trim() !== "") {
+ payload.password = form.password.trim();
+ }
+ form.transform(() => payload).put(route("admin.mail-profiles.update", editTarget.value.id), {
+ preserveScroll: true,
+ onSuccess: () => {
+ editOpen.value = false;
+ editTarget.value = null;
+ },
+ });
+}
+
function toggleActive(p) {
window.axios
.post(route("admin.mail-profiles.toggle", p.id))
@@ -160,9 +208,11 @@ const statusClass = (p) => {
Pošlji test
@@ -241,6 +291,61 @@ const statusClass = (p) => {
+
+
+ Uredi Mail profil
+
+
+
+
+
+
+
+
diff --git a/resources/js/Pages/Cases/Partials/ActivityTable.vue b/resources/js/Pages/Cases/Partials/ActivityTable.vue
index 3d769d2..79bc8b4 100644
--- a/resources/js/Pages/Cases/Partials/ActivityTable.vue
+++ b/resources/js/Pages/Cases/Partials/ActivityTable.vue
@@ -112,12 +112,7 @@ const confirmDeleteAction = () => {
{{ row.contract.reference }}
-
- {{ client_case?.person?.full_name || "—" }}
-
+ —
diff --git a/resources/js/Pages/FieldJob/Index.vue b/resources/js/Pages/FieldJob/Index.vue
index e4d31e6..19cf2f3 100644
--- a/resources/js/Pages/FieldJob/Index.vue
+++ b/resources/js/Pages/FieldJob/Index.vue
@@ -2,6 +2,7 @@
import AppLayout from "@/Layouts/AppLayout.vue";
import { Link, useForm } from "@inertiajs/vue3";
import { computed, ref, watch } from "vue";
+import DataTableClient from "@/Components/DataTable/DataTableClient.vue";
const props = defineProps({
setting: Object,
@@ -138,39 +139,129 @@ const assignedContractsFiltered = computed(() => {
return list.filter(matchesSearch);
});
-// Pagination state
+// DataTableClient state per table
+const unassignedSort = ref({ key: null, direction: null });
const unassignedPage = ref(1);
-const unassignedPerPage = ref(10);
+const unassignedPageSize = ref(10);
+const assignedSort = ref({ key: null, direction: null });
const assignedPage = ref(1);
-const assignedPerPage = ref(10);
+const assignedPageSize = ref(10);
-// Reset pages when filters change
-watch([search], () => {
+watch([search, assignedFilterUserId], () => {
unassignedPage.value = 1;
assignedPage.value = 1;
});
-watch([assignedFilterUserId], () => {
- assignedPage.value = 1;
-});
-// Paginated lists
-const unassignedTotal = computed(() => unassignedFiltered.value.length);
-const unassignedTotalPages = computed(() =>
- Math.max(1, Math.ceil(unassignedTotal.value / unassignedPerPage.value))
-);
-const unassignedPageItems = computed(() => {
- const start = (unassignedPage.value - 1) * unassignedPerPage.value;
- return unassignedFiltered.value.slice(start, start + unassignedPerPage.value);
-});
+// Column definitions for DataTableClient
+const unassignedColumns = [
+ { key: "reference", label: "Pogodba", sortable: true, class: "w-32" },
+ {
+ key: "case_person",
+ label: "Primer",
+ sortable: true,
+ formatter: (c) => c.client_case?.person?.full_name || "-",
+ },
+ {
+ key: "address",
+ label: "Naslov",
+ sortable: true,
+ formatter: (c) => primaryCaseAddress(c),
+ },
+ {
+ key: "client_person",
+ label: "Stranka",
+ sortable: true,
+ formatter: (c) => c.client?.person?.full_name || "-",
+ },
+ {
+ key: "start_date",
+ label: "Začetek",
+ sortable: true,
+ formatter: (c) => formatDate(c.start_date),
+ },
+ {
+ key: "balance_amount",
+ label: "Stanje",
+ align: "right",
+ sortable: true,
+ formatter: (c) => formatCurrencyEUR(c.account?.balance_amount),
+ },
+ { key: "_actions", label: "Dejanje", class: "w-32" },
+];
-const assignedTotal = computed(() => assignedContractsFiltered.value.length);
-const assignedTotalPages = computed(() =>
- Math.max(1, Math.ceil(assignedTotal.value / assignedPerPage.value))
+const assignedColumns = [
+ { key: "reference", label: "Pogodba", sortable: true, class: "w-32" },
+ {
+ key: "case_person",
+ label: "Primer",
+ sortable: true,
+ formatter: (c) => c.client_case?.person?.full_name || "-",
+ },
+ {
+ key: "address",
+ label: "Naslov",
+ sortable: true,
+ formatter: (c) => primaryCaseAddress(c),
+ },
+ {
+ key: "client_person",
+ label: "Stranka",
+ sortable: true,
+ formatter: (c) => c.client?.person?.full_name || "-",
+ },
+ {
+ key: "assigned_at",
+ label: "Dodeljeno dne",
+ sortable: true,
+ formatter: (c) => formatDate(props.assignments?.[c.uuid]?.assigned_at),
+ },
+ {
+ key: "assigned_to",
+ label: "Dodeljeno komu",
+ sortable: true,
+ formatter: (c) => assignedTo(c) || "-",
+ },
+ {
+ key: "balance_amount",
+ label: "Stanje",
+ align: "right",
+ sortable: true,
+ formatter: (c) => formatCurrencyEUR(c.account?.balance_amount),
+ },
+ { key: "_actions", label: "Dejanje", class: "w-32" },
+];
+
+// Provide derived row arrays for DataTable (already filtered)
+// Add a flat numeric property `balance_amount` so the generic table sorter can sort by value
+// (original data nests it under account.balance_amount which the sorter cannot reach).
+const unassignedRows = computed(() =>
+ unassignedFiltered.value.map((c) => ({
+ ...c,
+ // Ensure numeric so sorter treats it as number (server often returns string)
+ balance_amount:
+ c?.account?.balance_amount === null || c?.account?.balance_amount === undefined
+ ? null
+ : Number(c.account.balance_amount),
+ // Flatten derived text fields so DataTable sorting/searching works
+ case_person: c.client_case?.person?.full_name || null,
+ client_person: c.client?.person?.full_name || null,
+ address: primaryCaseAddress(c) || null,
+ assigned_to: null, // not assigned yet
+ }))
+);
+const assignedRows = computed(() =>
+ assignedContractsFiltered.value.map((c) => ({
+ ...c,
+ balance_amount:
+ c?.account?.balance_amount === null || c?.account?.balance_amount === undefined
+ ? null
+ : Number(c.account.balance_amount),
+ case_person: c.client_case?.person?.full_name || null,
+ client_person: c.client?.person?.full_name || null,
+ address: primaryCaseAddress(c) || null,
+ assigned_to: assignedTo(c) || null,
+ }))
);
-const assignedPageItems = computed(() => {
- const start = (assignedPage.value - 1) * assignedPerPage.value;
- return assignedContractsFiltered.value.slice(start, start + assignedPerPage.value);
-});
@@ -197,7 +288,7 @@ const assignedPageItems = computed(() => {
class="border rounded px-3 py-2 w-full max-w-xl"
/>
-
+
Pogodbe (nedodeljene)
@@ -217,98 +308,42 @@ const assignedPageItems = computed(() => {
{{ form.errors.assigned_user_id }}
-
-
-
-
- | Pogodba |
- Primer |
- Naslov |
- Stranka |
- Začetek |
- Stanje |
- Dejanje |
-
-
-
-
- | {{ c.reference }} |
-
-
- {{ c.client_case?.person?.full_name || "Primer stranke" }}
-
- {{ c.client_case?.person?.full_name || "-" }}
- |
- {{ primaryCaseAddress(c) }} |
-
- {{ c.client?.person?.full_name || "-" }}
- |
- {{ formatDate(c.start_date) }} |
-
- {{ formatCurrencyEUR(c.account?.balance_amount) }}
- |
-
-
- |
-
-
- | Ni najdenih pogodb. |
-
-
-
-
-
-
-
- Prikazano
- {{
- Math.min((unassignedPage - 1) * unassignedPerPage + 1, unassignedTotal)
- }}–{{ Math.min(unassignedPage * unassignedPerPage, unassignedTotal) }} od
- {{ unassignedTotal }}
-
-
-
-
- Stran {{ unassignedPage }} / {{ unassignedTotalPages }}
+ {{ row.client_case?.person?.full_name || "Primer stranke" }}
+
+ {{ row.client_case?.person?.full_name || "-" }}
+
+
-
-
+
+
+
+ Ni najdenih pogodb.
+
+
+
-
+
Dodeljene pogodbe
@@ -322,99 +357,45 @@ const assignedPageItems = computed(() => {
-
-
-
-
- | Pogodba |
- Primer |
- Naslov |
- Stranka |
- Dodeljeno dne |
- Dodeljeno komu |
- Stanje |
- Dejanje |
-
-
-
-
- | {{ c.reference }} |
-
-
- {{ c.client_case?.person?.full_name || "Primer stranke" }}
-
- {{ c.client_case?.person?.full_name || "-" }}
- |
- {{ primaryCaseAddress(c) }} |
-
- {{ c.client?.person?.full_name || "-" }}
- |
-
- {{ formatDate(props.assignments?.[c.uuid]?.assigned_at) }}
- |
- {{ assignedTo(c) || "-" }} |
-
- {{ formatCurrencyEUR(c.account?.balance_amount) }}
- |
-
-
- |
-
-
- |
- Ni dodeljenih pogodb za izbran filter.
- |
-
-
-
-
-
-
-
- Prikazano
- {{ Math.min((assignedPage - 1) * assignedPerPage + 1, assignedTotal) }}–{{
- Math.min(assignedPage * assignedPerPage, assignedTotal)
- }}
- od {{ assignedTotal }}
-
-
-
-
- Stran {{ assignedPage }} / {{ assignedTotalPages }}
+ {{ row.client_case?.person?.full_name || "Primer stranke" }}
+
+ {{ row.client_case?.person?.full_name || "-" }}
+
+
-
-
+
+
+
+ Ni dodeljenih pogodb za izbran filter.
+
+
+
|