feat: allowed more than one assigned person
This commit is contained in:
parent
983097ea7c
commit
83c0f449f5
@ -71,12 +71,10 @@ public function handle()
|
||||
$salesUser = User::where('name', $this->rowValue($row, 'sales'))->first();
|
||||
$pic = User::where('name', $this->rowValue($row, 'pic'))->first();
|
||||
if ($pic) {
|
||||
ClientUserAssignation::updateOrCreate(
|
||||
ClientUserAssignation::firstOrCreate(
|
||||
[
|
||||
'client_id' => $client->id,
|
||||
'role' => ClientUserAssignation::ROLE_ASSIGNED_PERSON,
|
||||
],
|
||||
[
|
||||
'user_id' => $pic->id,
|
||||
]
|
||||
);
|
||||
|
||||
@ -345,12 +345,10 @@ private function linkInvoiceClient(ClientInvoice $invoice, array $validated): Cl
|
||||
$client = Client::findOrFail($validated['client_id']);
|
||||
abort_unless($this->hierarchyService->canViewClient(Auth::user(), $client), 403);
|
||||
|
||||
ClientUserAssignation::updateOrCreate(
|
||||
ClientUserAssignation::firstOrCreate(
|
||||
[
|
||||
'client_id' => $client->id,
|
||||
'role' => ClientUserAssignation::ROLE_ASSIGNED_PERSON,
|
||||
],
|
||||
[
|
||||
'user_id' => Auth::id(),
|
||||
]
|
||||
);
|
||||
@ -621,12 +619,10 @@ public function storeClient(Request $request, ClientInvoice $invoice)
|
||||
[]
|
||||
);
|
||||
|
||||
ClientUserAssignation::updateOrCreate(
|
||||
ClientUserAssignation::firstOrCreate(
|
||||
[
|
||||
'client_id' => $client->id,
|
||||
'role' => ClientUserAssignation::ROLE_ASSIGNED_PERSON,
|
||||
],
|
||||
[
|
||||
'user_id' => Auth::id(),
|
||||
]
|
||||
);
|
||||
|
||||
@ -56,11 +56,17 @@ public function accounts()
|
||||
)
|
||||
->get();
|
||||
$customerMap = $localClients->map(function ($data) {
|
||||
$assignedPerson = $data->assignations->firstWhere('role', ClientUserAssignation::ROLE_ASSIGNED_PERSON);
|
||||
$assignedPeople = $data->assignations
|
||||
->where('role', ClientUserAssignation::ROLE_ASSIGNED_PERSON)
|
||||
->map(fn (ClientUserAssignation $assignation) => $assignation->user?->name)
|
||||
->filter()
|
||||
->values();
|
||||
$salesPerson = $data->assignations->firstWhere('role', ClientUserAssignation::ROLE_SALES_PERSON);
|
||||
$data['industry'] = $data->industry;
|
||||
$data['sql_acc_code'] = implode(',', $data->customers->pluck('sql_acc_code')->toArray());
|
||||
$data['assigned_person'] = $assignedPerson?->user?->name;
|
||||
$data['assigned_person'] = $assignedPeople->isNotEmpty()
|
||||
? $assignedPeople->implode(', ')
|
||||
: null;
|
||||
$data['sales_person'] = $salesPerson?->user?->name;
|
||||
$data['latest_remaining_amount'] = $data->latestRemainingAmount();
|
||||
|
||||
@ -174,7 +180,8 @@ public function updateAccount(Request $request, $id)
|
||||
'customer_id' => ['required', 'string', 'unique:clients,customer_id,'.$localClient->id],
|
||||
'industry' => ['nullable', 'string'],
|
||||
'sql_acc_code' => ['nullable', 'string'],
|
||||
'assigned_person' => ['nullable', 'integer', 'exists:users,id'],
|
||||
'assigned_person' => ['nullable', 'array'],
|
||||
'assigned_person.*' => ['integer', 'exists:users,id'],
|
||||
'sales_person' => ['nullable', 'integer', 'exists:users,id'],
|
||||
]);
|
||||
|
||||
@ -200,29 +207,60 @@ public function updateAccount(Request $request, $id)
|
||||
});
|
||||
}
|
||||
|
||||
$assignmentValues = [
|
||||
ClientUserAssignation::ROLE_ASSIGNED_PERSON => $validated['assigned_person'] ?? null,
|
||||
ClientUserAssignation::ROLE_SALES_PERSON => $validated['sales_person'] ?? null,
|
||||
];
|
||||
|
||||
foreach ($assignmentValues as $role => $userId) {
|
||||
if ($userId === null) {
|
||||
$localClient->assignations()->where('role', $role)->delete();
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$localClient->assignations()->updateOrCreate(
|
||||
['role' => $role],
|
||||
['user_id' => $userId]
|
||||
$this->syncAssignedPeople($localClient, $validated['assigned_person'] ?? []);
|
||||
$this->syncSingleAssignment(
|
||||
$localClient,
|
||||
ClientUserAssignation::ROLE_SALES_PERSON,
|
||||
$validated['sales_person'] ?? null,
|
||||
);
|
||||
}
|
||||
|
||||
return redirect()
|
||||
->route('google-ads.accounts.edit', ['id' => $localClient->customer_id])
|
||||
->with('message-info', 'Account details updated.');
|
||||
}
|
||||
|
||||
private function syncAssignedPeople(Client $client, array $userIds): void
|
||||
{
|
||||
$userIds = collect($userIds)
|
||||
->filter(fn ($userId) => is_numeric($userId))
|
||||
->map(fn ($userId) => (int) $userId)
|
||||
->unique()
|
||||
->values()
|
||||
->all();
|
||||
|
||||
$query = $client->assignations()
|
||||
->where('role', ClientUserAssignation::ROLE_ASSIGNED_PERSON);
|
||||
|
||||
if ($userIds === []) {
|
||||
$query->delete();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$query->whereNotIn('user_id', $userIds)->delete();
|
||||
|
||||
foreach ($userIds as $userId) {
|
||||
$client->assignations()->firstOrCreate([
|
||||
'role' => ClientUserAssignation::ROLE_ASSIGNED_PERSON,
|
||||
'user_id' => $userId,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
private function syncSingleAssignment(Client $client, int $role, ?int $userId): void
|
||||
{
|
||||
$client->assignations()->where('role', $role)->delete();
|
||||
|
||||
if ($userId === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
$client->assignations()->create([
|
||||
'role' => $role,
|
||||
'user_id' => $userId,
|
||||
]);
|
||||
}
|
||||
|
||||
public function campaigns($id)
|
||||
{
|
||||
$campaigns = $this->adsService->listCampaigns($id);
|
||||
@ -249,11 +287,15 @@ private function hydrateClient(array $account): array
|
||||
);
|
||||
// dd($localClient);
|
||||
$localClient->load(['assignations.user', 'invoices.payments.items.billingItemType','customers']);
|
||||
$assignments = $localClient->assignations
|
||||
->mapWithKeys(function (ClientUserAssignation $assignation) {
|
||||
return [$assignation->role => $assignation->user_id];
|
||||
})
|
||||
->toArray();
|
||||
$assignments = [
|
||||
ClientUserAssignation::ROLE_ASSIGNED_PERSON => $localClient->assignations
|
||||
->where('role', ClientUserAssignation::ROLE_ASSIGNED_PERSON)
|
||||
->pluck('user_id')
|
||||
->values()
|
||||
->all(),
|
||||
ClientUserAssignation::ROLE_SALES_PERSON => $localClient->assignations
|
||||
->firstWhere('role', ClientUserAssignation::ROLE_SALES_PERSON)?->user_id,
|
||||
];
|
||||
|
||||
$users = User::orderBy('name')
|
||||
->get(['id', 'name', 'email'])
|
||||
@ -420,12 +462,10 @@ public function insertCSVDataToDB()
|
||||
$salesUser = User::where('name', $row['sales'])->first();
|
||||
$pic = User::where('name', $row['pic'])->first();
|
||||
if ($pic) {
|
||||
ClientUserAssignation::updateOrCreate(
|
||||
ClientUserAssignation::firstOrCreate(
|
||||
[
|
||||
'client_id' => $client->id,
|
||||
'role' => ClientUserAssignation::ROLE_ASSIGNED_PERSON,
|
||||
],
|
||||
[
|
||||
'user_id' => $pic->id,
|
||||
]
|
||||
);
|
||||
|
||||
@ -25,11 +25,13 @@ public static function roles(): array
|
||||
'id' => self::ROLE_ASSIGNED_PERSON,
|
||||
'label' => 'Assigned Person',
|
||||
'field' => 'assigned_person',
|
||||
'multiple' => true,
|
||||
],
|
||||
[
|
||||
'id' => self::ROLE_SALES_PERSON,
|
||||
'label' => 'Sales Person',
|
||||
'field' => 'sales_person',
|
||||
'multiple' => false,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
@ -12,6 +12,7 @@ import {
|
||||
Text,
|
||||
TextInput,
|
||||
Select,
|
||||
MultiSelect,
|
||||
Title,
|
||||
Divider,
|
||||
} from "@mantine/core";
|
||||
@ -23,6 +24,7 @@ interface AssignmentRole {
|
||||
id: number;
|
||||
label: string;
|
||||
field: "assigned_person" | "sales_person";
|
||||
multiple?: boolean;
|
||||
}
|
||||
|
||||
interface SelectOption {
|
||||
@ -35,7 +37,7 @@ interface AccountFormData {
|
||||
customer_id: string;
|
||||
industry: string;
|
||||
sql_acc_code: string;
|
||||
assigned_person: string;
|
||||
assigned_person: string[];
|
||||
sales_person: string;
|
||||
}
|
||||
|
||||
@ -43,7 +45,7 @@ interface Props {
|
||||
id: string;
|
||||
client: Client;
|
||||
clientAssignmentRoles: AssignmentRole[];
|
||||
clientAssignments: Record<number, number | null>;
|
||||
clientAssignments: Record<number, number | number[] | null>;
|
||||
assignmentUsers: SelectOption[];
|
||||
}
|
||||
|
||||
@ -59,13 +61,24 @@ export default function Page({
|
||||
customer_id: client.customer_id ?? id,
|
||||
industry: client.industry ?? "",
|
||||
sql_acc_code: client.sql_acc_code ?? "",
|
||||
assigned_person: "",
|
||||
assigned_person: [],
|
||||
sales_person: "",
|
||||
};
|
||||
|
||||
clientAssignmentRoles.forEach((role) => {
|
||||
const value = clientAssignments?.[role.id];
|
||||
initialData[role.field] = value ? String(value) : "";
|
||||
|
||||
if (role.field === "assigned_person") {
|
||||
initialData.assigned_person = Array.isArray(value)
|
||||
? value.map(String)
|
||||
: value
|
||||
? [String(value)]
|
||||
: [];
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
initialData.sales_person = value ? String(value) : "";
|
||||
});
|
||||
|
||||
const form = useForm<AccountFormData>(initialData);
|
||||
@ -77,7 +90,9 @@ export default function Page({
|
||||
...data,
|
||||
industry: data.industry || null,
|
||||
sql_acc_code: data.sql_acc_code || null,
|
||||
assigned_person: parseInt(data.assigned_person) || null,
|
||||
assigned_person: data.assigned_person
|
||||
.map((userId) => parseInt(userId))
|
||||
.filter(Boolean),
|
||||
sales_person: parseInt(data.sales_person) || null,
|
||||
}));
|
||||
|
||||
@ -169,19 +184,33 @@ export default function Page({
|
||||
breakpoints={[{ maxWidth: "md", cols: 1 }]}
|
||||
spacing="md"
|
||||
>
|
||||
{clientAssignmentRoles.map((role) => (
|
||||
{clientAssignmentRoles.map((role) =>
|
||||
role.multiple || role.field === "assigned_person" ? (
|
||||
<MultiSelect
|
||||
key={role.id}
|
||||
label={role.label}
|
||||
placeholder={`Select ${role.label}`}
|
||||
data={assignmentUsers}
|
||||
value={form.data.assigned_person}
|
||||
onChange={(value) => form.setData("assigned_person", value)}
|
||||
error={form.errors.assigned_person}
|
||||
clearable
|
||||
searchable
|
||||
/>
|
||||
) : (
|
||||
<Select
|
||||
key={role.id}
|
||||
label={role.label}
|
||||
placeholder={`Select ${role.label}`}
|
||||
data={assignmentUsers}
|
||||
value={form.data[role.field]}
|
||||
onChange={(value) => form.setData(role.field, value ?? "")}
|
||||
error={form.errors[role.field]}
|
||||
value={form.data.sales_person}
|
||||
onChange={(value) => form.setData("sales_person", value ?? "")}
|
||||
error={form.errors.sales_person}
|
||||
clearable
|
||||
searchable
|
||||
/>
|
||||
))}
|
||||
),
|
||||
)}
|
||||
</SimpleGrid>
|
||||
</Stack>
|
||||
</Paper>
|
||||
|
||||
@ -132,20 +132,20 @@ export default function TicketDetails({
|
||||
return <Badge color={getStatusColor(value)}>{value}</Badge>;
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorKey: 'latest_remaining_amount',
|
||||
header: 'Remaining Amount',
|
||||
size: 180,
|
||||
Cell: ({ cell }: any) => {
|
||||
const amount = parseAmount(cell.getValue());
|
||||
// {
|
||||
// accessorKey: 'latest_remaining_amount',
|
||||
// header: 'Remaining Amount',
|
||||
// size: 180,
|
||||
// Cell: ({ cell }: any) => {
|
||||
// const amount = parseAmount(cell.getValue());
|
||||
|
||||
return (
|
||||
<Text color={amount < 0 ? 'red' : 'green'} weight={700}>
|
||||
{currencyFormatter.format(amount)}
|
||||
</Text>
|
||||
);
|
||||
},
|
||||
},
|
||||
// return (
|
||||
// <Text color={amount < 0 ? 'red' : 'green'} weight={700}>
|
||||
// {currencyFormatter.format(amount)}
|
||||
// </Text>
|
||||
// );
|
||||
// },
|
||||
// },
|
||||
{
|
||||
accessorKey: 'latest_start_date',
|
||||
header: 'Latest Start Date',
|
||||
|
||||
@ -85,7 +85,7 @@ interface Props {
|
||||
can_modify?: boolean;
|
||||
};
|
||||
clientAssignmentRoles: AssignmentRole[];
|
||||
clientAssignments: Record<number, number | null>;
|
||||
clientAssignments: Record<number, number | number[] | null>;
|
||||
assignmentUsers: SelectOption[];
|
||||
clientInvoices: ClientInvoice[];
|
||||
clientAdjustments: ClientInvoiceAdjustment[];
|
||||
@ -97,6 +97,7 @@ interface AssignmentRole {
|
||||
id: number;
|
||||
label: string;
|
||||
field: string;
|
||||
multiple?: boolean;
|
||||
}
|
||||
|
||||
interface SelectOption {
|
||||
@ -611,15 +612,22 @@ export default function TicketDetails({
|
||||
}, [activities]);
|
||||
|
||||
const lookupAssignmentLabel = (roleId: number) => {
|
||||
const userId = clientAssignments?.[roleId];
|
||||
if (!userId) {
|
||||
const userIds = clientAssignments?.[roleId];
|
||||
if (!userIds || (Array.isArray(userIds) && userIds.length === 0)) {
|
||||
return '—';
|
||||
}
|
||||
|
||||
return (
|
||||
assignmentUsers.find((option) => option.value === String(userId))
|
||||
?.label ?? '—'
|
||||
);
|
||||
const ids = Array.isArray(userIds) ? userIds : [userIds];
|
||||
const labels = ids
|
||||
.map(
|
||||
(userId) =>
|
||||
assignmentUsers.find(
|
||||
(option) => option.value === String(userId),
|
||||
)?.label,
|
||||
)
|
||||
.filter(Boolean);
|
||||
|
||||
return labels.length > 0 ? labels.join(', ') : '—';
|
||||
};
|
||||
|
||||
const accountSummary = [
|
||||
|
||||
4
resources/js/types/index.d.ts
vendored
4
resources/js/types/index.d.ts
vendored
@ -62,8 +62,8 @@ export interface Client {
|
||||
latest_start_date?: string | null;
|
||||
latest_end_date?: string | null;
|
||||
latest_remaining_days?: number | null;
|
||||
assigned_person?: string;
|
||||
sales_person?: string;
|
||||
assigned_person?: string | null;
|
||||
sales_person?: string | null;
|
||||
}
|
||||
|
||||
export interface Role {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user