diff --git a/app/Http/Controllers/Admin/PackageController.php b/app/Http/Controllers/Admin/PackageController.php
index 071dc88..a3493fd 100644
--- a/app/Http/Controllers/Admin/PackageController.php
+++ b/app/Http/Controllers/Admin/PackageController.php
@@ -12,6 +12,7 @@
use App\Models\SmsTemplate;
use App\Services\Contact\PhoneSelector;
use App\Services\Sms\SmsService;
+use Illuminate\Database\Eloquent\Builder;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Bus;
@@ -50,6 +51,7 @@ public function create(Request $request): Response
->get(['id', 'name', 'content']);
$segments = \App\Models\Segment::query()
->where('active', true)
+ ->where('exclude', false)
->orderBy('name')
->get(['id', 'name']);
// Provide a lightweight list of recent clients with person names for filtering
@@ -321,7 +323,6 @@ public function contracts(Request $request, PhoneSelector $selector): \Illuminat
$request->validate([
'segment_id' => ['nullable', 'integer', 'exists:segments,id'],
'q' => ['nullable', 'string'],
-
'client_id' => ['nullable', 'integer', 'exists:clients,id'],
'only_mobile' => ['nullable', 'boolean'],
'only_validated' => ['nullable', 'boolean'],
@@ -332,13 +333,13 @@ public function contracts(Request $request, PhoneSelector $selector): \Illuminat
]);
$segmentId = $request->input('segment_id') ? (int) $request->input('segment_id') : null;
-
$query = Contract::query()
->with([
'clientCase.person.phones',
'clientCase.client.person',
'account',
+ 'segments:id,name',
])
->select('contracts.*')
->latest('contracts.id');
@@ -350,6 +351,15 @@ public function contracts(Request $request, PhoneSelector $selector): \Illuminat
->where('contract_segment.segment_id', '=', $segmentId)
->where('contract_segment.active', true);
});
+ } else {
+ // Only include contracts that have at least one active, non-excluded segment
+ $query->whereExists(fn ($exist) => $exist->select(\DB::raw(1))
+ ->from('contract_segment')
+ ->join('segments', 'segments.id', '=', 'contract_segment.segment_id')
+ ->where('contract_segment.active', true)
+ ->where('segments.exclude', false)
+ ->whereColumn('contract_segment.contract_id', 'contracts.id')
+ );
}
if ($q = trim((string) $request->input('q'))) {
@@ -399,13 +409,14 @@ public function contracts(Request $request, PhoneSelector $selector): \Illuminat
});
}
- $contracts = $query->get();
+ $contracts = $query->limit(500)->get();
$data = collect($contracts)->map(function (Contract $contract) use ($selector) {
$person = $contract->clientCase?->person;
$selected = $person ? $selector->selectForPerson($person) : ['phone' => null, 'reason' => 'no_person'];
$phone = $selected['phone'];
$clientPerson = $contract->clientCase?->client?->person;
+ $segment = collect($contract->segments)->last();
return [
'id' => $contract->id,
@@ -423,6 +434,7 @@ public function contracts(Request $request, PhoneSelector $selector): \Illuminat
'uuid' => $person?->uuid,
'full_name' => $person?->full_name,
],
+ 'segment' => $segment,
// Stranka: the client person
'client' => $clientPerson ? [
'id' => $contract->clientCase?->client?->id,
@@ -440,7 +452,7 @@ public function contracts(Request $request, PhoneSelector $selector): \Illuminat
});
return response()->json([
- 'data' => $data
+ 'data' => $data,
]);
}
diff --git a/resources/js/Components/app/ui/AppRangeDatePicker.vue b/resources/js/Components/app/ui/AppRangeDatePicker.vue
new file mode 100644
index 0000000..7a831a5
--- /dev/null
+++ b/resources/js/Components/app/ui/AppRangeDatePicker.vue
@@ -0,0 +1,177 @@
+
+
+
+
+
+
+
+
+ {
+ if (calendarValue?.start?.toString() !== startDate?.toString()) {
+ calendarValue = { start: startDate, end: undefined };
+ }
+ }
+ "
+ />
+
+
+
diff --git a/resources/js/Pages/Admin/Packages/Create.vue b/resources/js/Pages/Admin/Packages/Create.vue
index 8d9b1a3..c9971db 100644
--- a/resources/js/Pages/Admin/Packages/Create.vue
+++ b/resources/js/Pages/Admin/Packages/Create.vue
@@ -2,6 +2,7 @@
import AdminLayout from "@/Layouts/AdminLayout.vue";
import { Link, router, useForm } from "@inertiajs/vue3";
import { ref, computed, nextTick } from "vue";
+import axios from "axios";
import {
Card,
CardContent,
@@ -39,6 +40,9 @@ import {
BadgeCheckIcon,
} from "lucide-vue-next";
import { fmtDateDMY } from "@/Utilities/functions";
+import { upperFirst } from "lodash";
+import AppCombobox from "@/Components/app/ui/AppCombobox.vue";
+import AppRangeDatePicker from "@/Components/app/ui/AppRangeDatePicker.vue";
const props = defineProps({
profiles: { type: Array, default: () => [] },
@@ -123,13 +127,19 @@ const contracts = ref({
const segmentId = ref(null);
const search = ref("");
const clientId = ref(null);
-const startDateFrom = ref("");
-const startDateTo = ref("");
-const promiseDateFrom = ref("");
-const promiseDateTo = ref("");
+const startDateRange = ref({ start: null, end: null });
+const promiseDateRange = ref({ start: null, end: null });
const onlyMobile = ref(false);
const onlyValidated = ref(false);
const loadingContracts = ref(false);
+
+// Transform clients for AppCombobox
+const clientItems = computed(() =>
+ props.clients.map((c) => ({
+ value: c.id,
+ label: c.name,
+ }))
+);
const selectedContractIds = ref(new Set());
const perPage = ref(25);
@@ -153,6 +163,11 @@ const contractColumns = [
accessorFn: (row) => row.selected_phone?.number || "—",
header: "Izbrana številka",
},
+ {
+ id: "segment",
+ accessorFn: (row) => upperFirst(row.segment?.name) || "—",
+ header: "Segment",
+ },
{ accessorKey: "no_phone_reason", header: "Opomba" },
];
@@ -175,19 +190,22 @@ async function loadContracts(url = null) {
if (segmentId.value) params.append("segment_id", segmentId.value);
if (search.value) params.append("q", search.value);
if (clientId.value) params.append("client_id", clientId.value);
- if (startDateFrom.value) params.append("start_date_from", startDateFrom.value);
- if (startDateTo.value) params.append("start_date_to", startDateTo.value);
- if (promiseDateFrom.value) params.append("promise_date_from", promiseDateFrom.value);
- if (promiseDateTo.value) params.append("promise_date_to", promiseDateTo.value);
+ if (startDateRange.value?.start)
+ params.append("start_date_from", startDateRange.value.start);
+ if (startDateRange.value?.end)
+ params.append("start_date_to", startDateRange.value.end);
+ if (promiseDateRange.value?.start)
+ params.append("promise_date_from", promiseDateRange.value.start);
+ if (promiseDateRange.value?.end)
+ params.append("promise_date_to", promiseDateRange.value.end);
if (onlyMobile.value) params.append("only_mobile", "1");
if (onlyValidated.value) params.append("only_validated", "1");
params.append("per_page", perPage.value);
const target = url || `${route("admin.packages.contracts")}?${params.toString()}`;
- const res = await fetch(target, {
+ const { data: json } = await axios.get(target, {
headers: { "X-Requested-With": "XMLHttpRequest" },
});
- const json = await res.json();
// Wait for next tick before updating to avoid Vue reconciliation issues
await nextTick();
@@ -238,10 +256,13 @@ function goToPage(page) {
if (segmentId.value) params.append("segment_id", segmentId.value);
if (search.value) params.append("q", search.value);
if (clientId.value) params.append("client_id", clientId.value);
- if (startDateFrom.value) params.append("start_date_from", startDateFrom.value);
- if (startDateTo.value) params.append("start_date_to", startDateTo.value);
- if (promiseDateFrom.value) params.append("promise_date_from", promiseDateFrom.value);
- if (promiseDateTo.value) params.append("promise_date_to", promiseDateTo.value);
+ if (startDateRange.value?.start)
+ params.append("start_date_from", startDateRange.value.start);
+ if (startDateRange.value?.end) params.append("start_date_to", startDateRange.value.end);
+ if (promiseDateRange.value?.start)
+ params.append("promise_date_from", promiseDateRange.value.start);
+ if (promiseDateRange.value?.end)
+ params.append("promise_date_to", promiseDateRange.value.end);
if (onlyMobile.value) params.append("only_mobile", "1");
if (onlyValidated.value) params.append("only_validated", "1");
params.append("per_page", perPage.value);
@@ -255,10 +276,8 @@ function resetFilters() {
segmentId.value = null;
clientId.value = null;
search.value = "";
- startDateFrom.value = "";
- startDateTo.value = "";
- promiseDateFrom.value = "";
- promiseDateTo.value = "";
+ startDateRange.value = { start: null, end: null };
+ promiseDateRange.value = { start: null, end: null };
onlyMobile.value = false;
onlyValidated.value = false;
contracts.value = {
@@ -448,9 +467,10 @@ const numbersCount = computed(() => {
(form.delivery_report = val)"
+ :model-value="form.delivery_report"
+ @update:model-value="(val) => (form.delivery_report = val)"
id="delivery-report"
+ :disabled="true"
/>
-
+
@@ -586,29 +604,21 @@ const numbersCount = computed(() => {
Datum začetka pogodbe
-
+
Datum obljube plačila
-
+
@@ -621,8 +631,8 @@ const numbersCount = computed(() => {
{
onlyMobile = val;
}
@@ -635,8 +645,8 @@ const numbersCount = computed(() => {
{
onlyValidated = val;
}
@@ -653,11 +663,11 @@ const numbersCount = computed(() => {
@@ -669,7 +679,7 @@ const numbersCount = computed(() => {
- Rezultati iskanja
+ Rezultati iskanja (do 500 zapisov)
Najdeno {{ contracts.meta.total }}
{{
@@ -689,7 +699,7 @@ const numbersCount = computed(() => {
variant="secondary"
class="text-sm"
>
-
+
Izbrano: {{ selectedContractIds.size }}
@@ -111,10 +111,6 @@ function confirmDelete() {
:meta="packages"
route-name="admin.packages.index"
>
-
- {{ row.uuid }}
-
-
{{ row.name ?? "—" }}
@@ -128,7 +124,9 @@ function confirmDelete() {
- {{ row.finished_at ?? "—" }}
+ {{
+ fmtDateTime(row.finished_at) ?? "—"
+ }}
@@ -157,8 +155,10 @@ function confirmDelete() {
Izbriši paket?
Ali ste prepričani, da želite izbrisati paket
- #{{ packageToDelete.id }} - {{ packageToDelete.name || 'Brez imena' }}?
- Tega dejanja ni mogoče razveljaviti.
+ #{{ packageToDelete.id }} -
+ {{ packageToDelete.name || "Brez imena" }}? Tega dejanja ni mogoče razveljaviti.
diff --git a/resources/js/Pages/Imports/Create.vue b/resources/js/Pages/Imports/Create.vue
index e512d2f..0e0662f 100644
--- a/resources/js/Pages/Imports/Create.vue
+++ b/resources/js/Pages/Imports/Create.vue
@@ -245,7 +245,7 @@ async function startImport() {
-
+