From 8770925309795df628fd1074121f29a58132d2ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anna-Sara=20S=C3=A9lea?= Date: Wed, 7 Jan 2026 17:20:04 +0100 Subject: [PATCH] Feature: Create groups --- app/Http/Controllers/CustomerController.php | 17 +- .../Controllers/CustomerGroupController.php | 142 +++++++ app/Http/Controllers/DepositController.php | 33 +- app/Http/Middleware/ApiToken.php | 27 ++ app/Models/Customer.php | 5 +- app/Models/CustomerGroup.php | 26 ++ app/Models/Tableversion.php | 13 + config/app.php | 5 + ...41_add_group_columns_to_customer_table.php | 30 ++ ...06_074517_create_customer_groups_table.php | 28 ++ ...2_30_075834_add_ids_to_customers_table.php | 28 ++ ...2_30_083850_create_tableversions_table.php | 29 ++ resources/css/app.scss | 42 ++ resources/js/Layouts/AuthenticatedLayout.tsx | 16 +- resources/js/Pages/Customer.tsx | 111 +++-- resources/js/Pages/CustomerGroups.tsx | 384 ++++++++++++++++++ resources/js/Pages/Dashboard.tsx | 51 +-- routes/api.php | 10 +- routes/console.php | 102 ++++- routes/web.php | 2 + 20 files changed, 1019 insertions(+), 82 deletions(-) create mode 100644 app/Http/Controllers/CustomerGroupController.php create mode 100644 app/Http/Middleware/ApiToken.php create mode 100644 app/Models/CustomerGroup.php create mode 100644 app/Models/Tableversion.php create mode 100644 database/migrations/2025_11_05_162341_add_group_columns_to_customer_table.php create mode 100644 database/migrations/2025_11_06_074517_create_customer_groups_table.php create mode 100644 database/migrations/2025_12_30_075834_add_ids_to_customers_table.php create mode 100644 database/migrations/2025_12_30_083850_create_tableversions_table.php create mode 100644 resources/js/Pages/CustomerGroups.tsx diff --git a/app/Http/Controllers/CustomerController.php b/app/Http/Controllers/CustomerController.php index a537af2..732b94f 100644 --- a/app/Http/Controllers/CustomerController.php +++ b/app/Http/Controllers/CustomerController.php @@ -3,6 +3,7 @@ namespace App\Http\Controllers; use App\Models\Customer; +use App\Models\CustomerGroup; use Illuminate\Http\Request; use App\Models\Purchase; use Inertia\Inertia; @@ -14,7 +15,8 @@ class CustomerController extends Controller */ public function index() { - $customers = Customer::orderBy('name', 'asc')->get(); + $customers = Customer::where('is_in_group', 0)->orderBy('name', 'asc')->get(); + return Inertia::render('Dashboard', ['customers' => $customers]); } @@ -52,16 +54,22 @@ class CustomerController extends Controller public function show($id) { $customer = Customer::with('purchases')->with('deposits')->findOrFail($id); + $groupmembers = Customer::where('is_in_group', 1)->where('customer_group_id', $customer->customer_group_id)->get(); - return Inertia::render('Customer', ['customer' => $customer]); + + return Inertia::render('Customer', ['customer' => $customer, 'groupmembers' => $groupmembers]); } /** * Show the form for editing the specified resource. */ - public function edit(Customers $customers) + public function edit($id) { - // + $customer = Customer::findOrFail($id); + + $customer->customer_group_id = null; + $customer->is_in_group = 0; + $customer->save(); } /** @@ -112,4 +120,5 @@ class CustomerController extends Controller 'success' => true, 'message' => 'Customer deleted successfully' ]); } + } diff --git a/app/Http/Controllers/CustomerGroupController.php b/app/Http/Controllers/CustomerGroupController.php new file mode 100644 index 0000000..c09654e --- /dev/null +++ b/app/Http/Controllers/CustomerGroupController.php @@ -0,0 +1,142 @@ +get(); + $customers = Customer::whereNull('customer_group_id')->where('is_in_group', false)->orderBy('name', 'asc')->get(); + + return Inertia::render('CustomerGroups', ['groups' => $groups, 'customers' => $customers]); + } + + /** + * Show the form for creating a new resource. + */ + public function create() + { + // + } + + /** + * Store a newly created resource in storage. + */ + public function store(Request $request) + { + + $request->validate([ + 'group_name' => 'required', + 'customers' => 'required', + ]); + + $customers = $request->customers; + + $guardian_name = Customer::where('id', (int) $customers[0])->pluck('guardian_name'); + + $customerGroup = CustomerGroup::create([ + 'name' => $request->group_name, + ]); + + + $groupCustomer = Customer::create([ + 'name' => $request->group_name, + 'guardian_name' => $guardian_name[0], + 'is_in_group' => false, + 'customer_group_id' => $customerGroup->id + ]); + + $groupAmount= 0; + + foreach ($customers as $customerItem) { + $customer = Customer::findOrFail($customerItem); + $customer->customer_group_id = $customerGroup->id; + $customer->is_in_group = 1; + $groupAmount += $customer->deposit; + $customer->deposit = 0; + $customer->save(); + } + + Deposit::create([ + 'customer_id' => $groupCustomer->id, + 'amount' => $groupAmount, + ]); + + $groupCustomer->amount_left = $groupAmount; + $groupCustomer->deposit = $groupAmount; + $groupCustomer->save(); + + return redirect('customer-groups/'); + + } + + /** + * Display the specified resource. + */ + public function show(CustomerGroup $customerGroup) + { + // + } + + /** + * Show the form for editing the specified resource. + */ + public function edit(CustomerGroup $customerGroup) + { + // + } + + /** + * Update the specified resource in storage. + */ + public function update($id, Request $request) + { + + $request->validate([ + 'customers' => 'required', + ]); + + $customers = $request->customers; + $groupCustomer = Customer::where('customer_group_id', $id)->where('is_in_group', 0)->first(); + + foreach ($customers as $customerItem) { + $customer = Customer::findOrFail($customerItem); + $groupCustomer->deposit += $customer->deposit; + $groupCustomer->amount_left += $customer->deposit; + $groupCustomer->save(); + $customer->customer_group_id = $id; + $customer->is_in_group = 1; + $customer->deposit = 0; + $customer->save(); + } + + return response()->json([ + 'success' => true, 'message' => 'Customer group was updated' + ]); + } + + /** + * Remove the specified resource from storage. + */ + public function destroy($id) + { + $group = CustomerGroup::findOrFail( $id ); + $customerGroup = Customer::where('customer_group_id', $group->id); + $customerGroup->delete(); + $group->delete(); + + return response()->json([ + 'success' => true, 'message' => 'Customer group deleted successfully' + ]); + } +} diff --git a/app/Http/Controllers/DepositController.php b/app/Http/Controllers/DepositController.php index d93977b..24319c9 100644 --- a/app/Http/Controllers/DepositController.php +++ b/app/Http/Controllers/DepositController.php @@ -41,11 +41,36 @@ class DepositController extends Controller ]); $customer = Customer::findOrFail($request->customer_id); - $customer->deposit = $customer->deposit + $request->deposit; - $customer->amount_left = $customer->amount_left + $request->deposit; - $customer->save(); - return redirect('customer/' . $request->customer_id); + if ($customer->is_in_group) { + $groupCustomer = Customer::where('customer_group_id', $customer->customer_group_id)->where('is_in_group', 0)->first(); + $groupCustomer->deposit += $customer->deposit; + $groupCustomer->amount_left += $request->deposit; + $groupCustomer->save(); + $customer->deposit = 0; + $customer->save(); + + if ($request->manual_deposit === 1) { + return redirect('customer/' . $request->customer_id); + } + return response()->json([ + 'success' => true, 'message' => 'Deposit added successfully' + ]); + + + } else { + $customer->deposit = $customer->deposit + $request->deposit; + $customer->amount_left = $customer->amount_left + $request->deposit; + $customer->save(); + + if ($request->manual_deposit === 1) { + return redirect('customer/' . $request->customer_id); + } + return response()->json([ + 'success' => true, 'message' => 'Deposit added successfully' + ]); + } + } /** diff --git a/app/Http/Middleware/ApiToken.php b/app/Http/Middleware/ApiToken.php new file mode 100644 index 0000000..4d1efb7 --- /dev/null +++ b/app/Http/Middleware/ApiToken.php @@ -0,0 +1,27 @@ +header('X-API-KEY') === config('app.apikey_deposit')) { + + return $next($request); + + } else { + return response()->json(['code' => 401, 'message' => 'Unauthorized']); + } + + } +} diff --git a/app/Models/Customer.php b/app/Models/Customer.php index 469299b..8a3b4f8 100644 --- a/app/Models/Customer.php +++ b/app/Models/Customer.php @@ -14,13 +14,16 @@ class Customer extends Model * @var array */ protected $fillable = [ + 'lan_id', 'name', 'guardian_name', 'amount left', 'amount used', 'deposit', 'give_leftover', - 'comment' + 'comment', + 'customer_group_id', + 'is_in_group' ]; /** diff --git a/app/Models/CustomerGroup.php b/app/Models/CustomerGroup.php new file mode 100644 index 0000000..1c0a4f5 --- /dev/null +++ b/app/Models/CustomerGroup.php @@ -0,0 +1,26 @@ + + */ + protected $fillable = [ + 'name', + ]; + + /** + * Get the customers for the group. + */ + public function customers() + { + return $this->hasMany(Customer::class); + } +} diff --git a/app/Models/Tableversion.php b/app/Models/Tableversion.php new file mode 100644 index 0000000..4d032a7 --- /dev/null +++ b/app/Models/Tableversion.php @@ -0,0 +1,13 @@ + env('APP_MAINTENANCE_STORE', 'database'), ], + 'apikey_deposit' => env('API_KEY_DEPOSIT'), + 'apilan_key' => env('API_LAN_KEY'), + 'apilan_url' => env('API_LAN_URL'), + 'apilan_clientcert_path' => env('API_LAN_CLIENTCERT_PATH') + ]; diff --git a/database/migrations/2025_11_05_162341_add_group_columns_to_customer_table.php b/database/migrations/2025_11_05_162341_add_group_columns_to_customer_table.php new file mode 100644 index 0000000..7646b62 --- /dev/null +++ b/database/migrations/2025_11_05_162341_add_group_columns_to_customer_table.php @@ -0,0 +1,30 @@ +boolean('is_in_group')->default(false); + $table->integer('customer_group_id')->nullable(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('customers', function (Blueprint $table) { + $table->dropColumn('is_in_group'); + $table->dropColumn('customer_group_id'); + }); + } +}; diff --git a/database/migrations/2025_11_06_074517_create_customer_groups_table.php b/database/migrations/2025_11_06_074517_create_customer_groups_table.php new file mode 100644 index 0000000..af1a684 --- /dev/null +++ b/database/migrations/2025_11_06_074517_create_customer_groups_table.php @@ -0,0 +1,28 @@ +id(); + $table->string('name'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('customer_groups'); + } +}; diff --git a/database/migrations/2025_12_30_075834_add_ids_to_customers_table.php b/database/migrations/2025_12_30_075834_add_ids_to_customers_table.php new file mode 100644 index 0000000..d7d7c90 --- /dev/null +++ b/database/migrations/2025_12_30_075834_add_ids_to_customers_table.php @@ -0,0 +1,28 @@ +integer('lan_id')->nullable(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('customers', function (Blueprint $table) { + $table->dropColumn('lan_id'); + }); + } +}; diff --git a/database/migrations/2025_12_30_083850_create_tableversions_table.php b/database/migrations/2025_12_30_083850_create_tableversions_table.php new file mode 100644 index 0000000..97e1984 --- /dev/null +++ b/database/migrations/2025_12_30_083850_create_tableversions_table.php @@ -0,0 +1,29 @@ +id(); + $table->string('table')->nullable(); + $table->integer('version')->nullable(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('tableversions'); + } +}; diff --git a/resources/css/app.scss b/resources/css/app.scss index 23b9c82..a0fae7e 100644 --- a/resources/css/app.scss +++ b/resources/css/app.scss @@ -91,6 +91,10 @@ details { right: 1em; background: #ffffff; + &.group { + top: 0.5em; + } + svg { display: block; } @@ -113,3 +117,41 @@ details { display: none; } } + +.customer-box:nth-child(even) { + background-color:rgba(0, 128, 187, 0.075) +} + +.customers-list { + max-height: 200px; + overflow-y: scroll; + padding: 5px;} + + +.add-customer-to-group-modal { + //display: none; + position: fixed; + z-index: 1; + padding-top: 100px; + left: 0; + top: 0; + width: 100%; + height: 100%; + overflow: auto; + background-color: rgb(0,0,0); + background-color: rgba(0,0,0,0.4); +} + +.add-customer-to-group-modal-content { + background-color: #fefefe; + margin: auto; + padding: 40px; + width: 60%; + position: relative; + + .delete { + position: absolute; + top: 20px; + right: 20px; + } +} \ No newline at end of file diff --git a/resources/js/Layouts/AuthenticatedLayout.tsx b/resources/js/Layouts/AuthenticatedLayout.tsx index 36b267b..07dc2b8 100644 --- a/resources/js/Layouts/AuthenticatedLayout.tsx +++ b/resources/js/Layouts/AuthenticatedLayout.tsx @@ -24,15 +24,23 @@ export default function Authenticated({ -{/* +
- Dashboard + Deltagare -
*/} + +
+ + Grupper + +
diff --git a/resources/js/Pages/Customer.tsx b/resources/js/Pages/Customer.tsx index 6de2032..272da57 100644 --- a/resources/js/Pages/Customer.tsx +++ b/resources/js/Pages/Customer.tsx @@ -1,6 +1,5 @@ import TextInput from '@/Components/TextInput'; import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout'; -import { Textarea } from '@headlessui/react'; import { Head, useForm } from '@inertiajs/react'; import axios from 'axios'; import { FormEventHandler } from 'react'; @@ -8,6 +7,7 @@ import { FormEventHandler } from 'react'; interface CustomerProps { customer:{ id: number + lan_id: number, name: string deposit: number amount_left: number @@ -25,16 +25,31 @@ interface CustomerProps { } }; -export default function Customer({customer}: CustomerProps) { +interface GroupmembersProps { + groupmembers:[{ + id: number + lan_id: number, + name: string + deposit: number + amount_left: number + give_leftover: number + guardian_name: string + comment: string; + }] +}; + +export default function Customer({customer, groupmembers}: (CustomerProps & GroupmembersProps) ) { const { data, setData, post, processing, errors, reset } = useForm({ amount: "", customer_id: customer.id, deposit: "", id: customer.id, - comment: "" + comment: "", + manual_deposit: 0 }); + const submit: FormEventHandler = (e) => { e.preventDefault() post(route('register_purchase'), { @@ -47,10 +62,11 @@ export default function Customer({customer}: CustomerProps) { const submitDeposit: FormEventHandler = (e) => { e.preventDefault() post(route('register_deposit'), { + headers: {'X-API-KEY': '123', 'Accept': 'application/json', 'Content-Type': 'application/json'}, onFinish: () => setData( 'deposit', '' - ), - }); + ), + }) } const updateComment: FormEventHandler = (e) => { @@ -73,13 +89,24 @@ export default function Customer({customer}: CustomerProps) {
-

{customer.name}

+ Tillbaka +

{customer.lan_id ? customer.lan_id + "." : "Grupp"} {customer.name}

-

Saldo: {customer.amount_left ? customer.amount_left : 0} kr

+

Saldo: {customer.amount_left ? customer.amount_left : 0} kr

Inbetalad summa: {customer.deposit ? customer.deposit : 0} kr

Vårnadshavare: {customer.guardian_name}

Ge ev överblivet saldo till vBytes: {customer.give_leftover ? "Ja" : "Nej"}

+ {groupmembers.length > 0 && + <> +

Gruppmedlemmar

+ {groupmembers && groupmembers.map( member => { + console.log(member) + return

{member.lan_id}. {member.name}

+ + })} + + }
@@ -144,44 +171,44 @@ export default function Customer({customer}: CustomerProps) {
- -
- - Inbetalning Swish/kontant -
- - - -
-
-
-
-
- setData('deposit', e.target.value)} - /> + {groupmembers.length < 1 && +
+ + Inbetalning Swish/kontant +
+ + +
-
-
-
- + + +
+
+ [setData('deposit', e.target.value), setData('manual_deposit', 1)]} + /> +
+
+
+ +
+
+ + +
+ + +
- - -
- - - -
-
- + + }
diff --git a/resources/js/Pages/CustomerGroups.tsx b/resources/js/Pages/CustomerGroups.tsx new file mode 100644 index 0000000..500f238 --- /dev/null +++ b/resources/js/Pages/CustomerGroups.tsx @@ -0,0 +1,384 @@ +import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout'; +import TextInput from '@/Components/TextInput'; +import { Head, useForm } from '@inertiajs/react'; +import { FormEventHandler, SetStateAction, useState } from 'react'; +import axios from 'axios'; +import Customer from './Customer'; + + +interface CustomerProps { + customers: [{ + id: number + name: string + customer_group_id: string, + is_in_group: boolean + }], +}; + +type Customer = { + id: number + name: string + customer_group_id: string, + is_in_group: boolean + +}; + +interface CustomerGroupProps { + groups: [{ + name: string, + id: number, + customers: [{ + id: number + name: string + customer_group_id: string, + is_in_group: boolean + }], + }] +}; + +type CustomerGroup = { + name: string, + id: number, + customers: [{ + id: number + name: string + customer_group_id: string, + is_in_group: boolean + }], +}; + +type FormInputs = { + id: number | null; + customers: Customer[], + group_name: string, +}; + +interface CurrentGroup { + id?: number | null ; + name?: string; +} + +export default function CustomerGroups({groups, customers} :( CustomerGroupProps & CustomerProps) ) { + const [addCustomerToGroupModal, setAddCustomerToGroupModal] = useState(false); + const [currentGroup, setCurrentGroup] = useState({id: null, name: ""}); + const [searchItemGroup, setSearchItemGroup] = useState(''); + const [searchItemCustomers, setSearchItemCustomers] = useState('') + const [searchItemCustomersAddToGroup, setSearchItemCustomersAddToGroup] = useState('') + const [uniqueGroupNameError, setUniqueGroupNameError,] = useState(false) + const [activeTabLetter, setActiveTabLetter] = useState('') + const [activeTab, setActiveTab] = useState('groups') + const [filteredCustomers, setFilteredCustomers] = useState(customers) + const [filteredCustomersAddToGroup, setFilteredCustomersAddToGroup] = useState(customers) + const [filteredGroups, setFilteredGroups] = useState(groups) + const [checkboxes, setCheckboxes] = useState([]); + const [checkboxesAddToGroup, setCheckboxesAddToGroup] = useState([]); + const letterArray = ["A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z","Å","Ä","Ö"]; + + const { data, setData, post, processing, errors, reset } = useForm({ + id: null, + group_name: "", + customers: [] + }); + + //Group + const handleInputChangeCreateGroup = (e: any) => { + const searchTerm = e.target.value; + setSearchItemCustomers(searchTerm) + + const filteredItems = customers.filter((customer) => + customer.name.toLowerCase().includes(searchTerm.toLowerCase()) + ); + + setFilteredCustomers(filteredItems); + } + + const submitGroup: FormEventHandler = (e : any) => { + e.preventDefault() + let submit = true; + groups.map( group => { + if (data.group_name.toLowerCase() === group.name.toLowerCase()) { + setUniqueGroupNameError(true); + submit = false; + } + }) + + if (submit) { + post(route('register_customer_group'), { + onFinish: () => [setData( + 'group_name', '', + + ), setCheckboxes([]), + window.location.href = "/customer-groups"], + + }); + } + } + + const handleInputChangeGroup = (e: any) => { + const searchTerm = e.target.value; + setSearchItemGroup(searchTerm) + + const filteredItems = groups.filter((customer) => + customer.name.toLowerCase().includes(searchTerm.toLowerCase()) + ); + + setFilteredGroups(filteredItems); + } + + + const handleInputClick = (searchTerm: string) => { + const filteredItems = groups.filter((customer) => + customer.name.toLowerCase().startsWith(searchTerm.toLowerCase()) + ); + + setFilteredGroups(filteredItems); + } + + const handleCheckBoxChange = (e: any) => { + let index = e.target.dataset.id; + let prev = checkboxes; + let itemIndex = prev.indexOf(index); + if (itemIndex !== -1) { + prev.splice(itemIndex, 1); + } else { + prev.push(index); + } + setCheckboxes([...prev]); + setData('customers', checkboxes) + } + + const deleteCustomerGroup = (id: string | number | undefined) => { + axios.delete('/api/customer-group/' + id) + .then(response => { + window.location.href = "/customer-groups"; + }) + .catch(error => {console.log(error)}) + } + + // Add to group + const handleInputChangeAddToGroup = (e: any) => { + const searchTerm = e.target.value; + setSearchItemCustomersAddToGroup(searchTerm) + + const filteredItems = customers.filter((customer) => + customer.name.toLowerCase().includes(searchTerm.toLowerCase()) + ); + + setFilteredCustomersAddToGroup(filteredItems); + } + + const addToGroup = (id: number | null | undefined) => { + axios.post('/api/add-to-customer-group/' + id, {customers: data.customers}) + .then(response => { + window.location.href = "/customer-groups"; + }) + .catch(error => {console.log(error)}) + } + + const handleCheckBoxChangeAddToGroup = (e: any) => { + let index = e.target.dataset.id; + let prev = checkboxesAddToGroup; + let itemIndex = prev.indexOf(index); + if (itemIndex !== -1) { + prev.splice(itemIndex, 1); + } else { + prev.push(index); + } + setCheckboxesAddToGroup([...prev]); + setData('customers', checkboxesAddToGroup) + } + + const deleteCustomerFromGroup = (id: string | number) => { + axios.put('/api/customer/' + id) + .then(response => { + window.location.href = "/customer-groups"; + }) + .catch(error => {console.log(error)}) + } + + + return ( + + + { addCustomerToGroupModal && +
+
+ +
+

Lägg till deltagare i grupp {currentGroup.name}

+
+ {filteredCustomersAddToGroup && filteredCustomersAddToGroup + .map( customer => { + return + })} +
+ +
+ Valda deltagare/funktionärer: + {filteredCustomersAddToGroup && filteredCustomersAddToGroup + .filter((filteredCustomer) => checkboxesAddToGroup.includes("" + filteredCustomer.id)) + .map( customer => { + return

+ {customer.name} +

+ + })} +
+ +
+
+
+ } +
+
+ Tillbaka + + {activeTab === "create-group" && <> +

Skapa grupp

+
+
+
+

Namn på grupp:

+ setData('group_name', e.target.value)} + /> + {uniqueGroupNameError &&

Gruppnamn är inte unikt

} + +

Välj vilka som ska ingå i gruppen:

+ +
+ {filteredCustomers && filteredCustomers + .map( customer => { + return + })} +
+ +
+ Valda deltagare/funktionärer: + {filteredCustomers && filteredCustomers + .filter((filteredCustomer) => checkboxes.includes("" + filteredCustomer.id)) + .map( customer => { + return

+ {customer.name} +

+ + })} +
+ + +
+
+ + } + + { activeTab === "groups" && <> +

Grupper

+ +
+
    + { letterArray && letterArray.map( letter => { + return
  • [setActiveTabLetter(letter) ,handleInputClick(letter)]}> + {letter} +
  • + }) + } +
  • [setActiveTabLetter(""), setFilteredGroups(groups), setSearchItemGroup("") ]}> + Rensa +
  • + +
+
+
+ + {filteredGroups && filteredGroups.map( group => { + return
+ + {group.name} +
+ + + +
+
+ + { group.customers.length < 2 && + + } +
    + { group.customers.length > 0 && group.customers.filter((customer) => customer.is_in_group).map( customer => { + return
    +

    {customer.name}

    + +
    + })} + +
+ +
+ + + +
+
+ })} +
+ + + } + + +
+
+ +
+ ); +} diff --git a/resources/js/Pages/Dashboard.tsx b/resources/js/Pages/Dashboard.tsx index c248282..1ef7abe 100644 --- a/resources/js/Pages/Dashboard.tsx +++ b/resources/js/Pages/Dashboard.tsx @@ -1,6 +1,6 @@ import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout'; -import { Head } from '@inertiajs/react'; -import { SetStateAction, useState } from 'react'; +import { Head, useForm } from '@inertiajs/react'; +import {useState } from 'react'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { faArrowRight } from '@fortawesome/free-solid-svg-icons' @@ -9,41 +9,46 @@ interface CustomerProps { customers: [{ id: number name: string + group_id: string, + is_group: boolean }], }; -interface Customer { +type Customer = { id: number name: string + group_id: string, + is_group: boolean + }; - -export default function Dashboard({customers}: CustomerProps) { +export default function Dashboard({ customers }: CustomerProps) { const [searchItem, setSearchItem] = useState('') const [activeTab, setActiveTab] = useState('') const [filteredCustomers, setFilteredCustomers] = useState(customers) - const letterArray = ["A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z","Å","Ä","Ö"]; + const letterArray = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "Å", "Ä", "Ö"]; - const handleInputChange = (e: any) => { + const handleInputChange = (e: any) => { const searchTerm = e.target.value; setSearchItem(searchTerm) const filteredItems = customers.filter((customer) => customer.name.toLowerCase().includes(searchTerm.toLowerCase()) ); - + setFilteredCustomers(filteredItems); } - const handleInputClick = (searchTerm: any) => { + const handleInputClick = (searchTerm: any) => { const filteredItems = customers.filter((customer) => customer.name.toLowerCase().startsWith(searchTerm.toLowerCase()) ); - + setFilteredCustomers(filteredItems); } + return ( @@ -59,22 +64,22 @@ export default function Dashboard({customers}: CustomerProps) { placeholder='Skriv för att söka' />
-
    - { letterArray && letterArray.map( letter => { - return
  • [setActiveTab(letter) ,handleInputClick(letter)]}> - {letter} -
  • +
      + {letterArray && letterArray.map(letter => { + return
    • [setActiveTab(letter), handleInputClick(letter)]}> + {letter} +
    • }) - } -
    • [setActiveTab(""), setFilteredCustomers(customers), setSearchItem("") ]}> - Rensa -
    • - + } +
    • [setActiveTab(""), setFilteredCustomers(customers), setSearchItem("")]}> + Rensa +
    • +
- + ); } diff --git a/routes/api.php b/routes/api.php index 0889057..91bf654 100644 --- a/routes/api.php +++ b/routes/api.php @@ -3,14 +3,22 @@ use Illuminate\Http\Request; use Illuminate\Support\Facades\Route; use App\Http\Controllers\CustomerController; +use App\Http\Controllers\CustomerGroupController; use App\Http\Controllers\PurchaseController; use App\Http\Controllers\DepositController; +use App\Http\Middleware\ApiToken; Route::post('register_customer', [CustomerController::class, 'store'])->name('register_customer'); Route::middleware('auth:sanctum')->group(function () { - Route::post('register_deposit', [DepositController::class, 'store'])->name('register_deposit'); + //Route::post('register_deposit', [DepositController::class, 'store'])->name('register_deposit'); Route::post('update_comment', [CustomerController::class, 'updateComment'])->name('update_comment'); Route::post('register_purchase', [PurchaseController::class, 'store'])->name('register_purchase'); + Route::post('customer-group', [CustomerGroupController::class, 'store'])->name('register_customer_group'); + Route::post('add-to-customer-group/{id}', [CustomerGroupController::class, 'update'])->name('add_to_customer_group'); + Route::delete('customer-group/{id}', [CustomerGroupController::class, 'destroy'])->name('delete_customer_group'); Route::delete('customer/{id}', [CustomerController::class, 'destroy'])->name('delete_customer'); + Route::put('customer/{id}', [CustomerController::class, 'edit'])->name('edit_customer'); }); + +Route::post('register_deposit', [DepositController::class, 'store'])->name('register_deposit')->middleware([ApiToken::class]); diff --git a/routes/console.php b/routes/console.php index 3c9adf1..5c8bedb 100644 --- a/routes/console.php +++ b/routes/console.php @@ -2,7 +2,103 @@ use Illuminate\Foundation\Inspiring; use Illuminate\Support\Facades\Artisan; +use Illuminate\Support\Facades\Schedule; +use GuzzleHttp\Client; +use Illuminate\Support\Facades\Storage; +use App\Models\Customer; +use App\Models\Tableversion; + +Schedule::call(function () { -Artisan::command('inspire', function () { - $this->comment(Inspiring::quote()); -})->purpose('Display an inspiring quote'); + $latestVersionParticipant = Tableversion::where('table', 'participants')->latest()->first(); + $latestVersionVolunteer = Tableversion::where('table', 'volunteers')->latest()->first(); + + $client = new Client(); + + $responseVersions= $client->request( + 'GET', + config('app.apilan_url') . "version", + [ + 'headers'=> [ + 'X-Api-Key' => config('app.apilan_key'), + 'Accept' => 'application/json', + 'Content-Type' => 'application/json' + ], + //'cert' => config('app.apilan_clientcert_path') + 'cert' => Storage::disk('public')->path('lan.vbytes.se.pem') + ], + ); + $versions = json_decode((string) $responseVersions->getBody(), true); + + if ($latestVersionParticipant === null ) { + Tableversion::create([ + 'table' => 'participants', + 'version' => $versions['participants'] - 1, + ]); + $latestVersionParticipant = Tableversion::where('table', 'participants')->latest()->first(); + } + + if ( $latestVersionVolunteer === null ) { + Tableversion::create([ + 'table' => 'volunteers', + 'version' => $versions['volunteers'] - 1, + ]); + $latestVersionVolunteer = Tableversion::where('table', 'volunteers')->latest()->first(); + } + + + if($latestVersionParticipant->version < $versions['participants'] || $latestVersionVolunteer->version < $versions['volunteers'] ) { + $response = $client->request( + 'GET', + config('app.apilan_url') . "data", + [ + 'headers'=> [ + 'X-Api-Key' => config('app.apilan_key'), + 'Accept' => 'application/json', + 'Content-Type' => 'application/json' + ], + //'cert' => config('app.apilan_clientcert_path') + 'cert' => Storage::disk('public')->path('lan.vbytes.se.pem') + ], + ); + $response_data = json_decode((string) $response->getBody(), true); + + foreach ($response_data['participants'] as $participant) { + Customer::updateOrCreate( + ['lan_id' => $participant['lan_id']], + [ + 'lan_id' => $participant['lan_id'], + 'name' => $participant['first_name'] . " " . $participant['surname'], + 'guardian_name' => "Anna", + ] + ); + } + + foreach ($response_data['volunteers'] as $volunteer) { + Customer::updateOrCreate( + ['lan_id' => $volunteer['lan_id']], + [ + 'lan_id' => $volunteer['lan_id'], + 'name' => $volunteer['first_name'] . " " . $volunteer['surname'], + 'guardian_name' => "Anna", + ] + ); + } + + + if($latestVersionParticipant->version < $versions['participants']) { + Tableversion::create([ + 'table' => 'participants', + 'version' =>$latestVersionParticipant->version + 1, + ]); + } + + if($latestVersionVolunteer->version < $versions['volunteers']) { + Tableversion::create([ + 'table' => 'volunteers', + 'version' =>$latestVersionVolunteer->version + 1, + ]); + } + + } +})->everyMinute(); diff --git a/routes/web.php b/routes/web.php index d8e35e2..c53c32c 100644 --- a/routes/web.php +++ b/routes/web.php @@ -5,6 +5,7 @@ use Illuminate\Foundation\Application; use Illuminate\Support\Facades\Route; use Inertia\Inertia; use App\Http\Controllers\CustomerController; +use App\Http\Controllers\CustomerGroupController; Route::get('/', function () { return Inertia::render('Auth/Login'); @@ -12,6 +13,7 @@ Route::get('/', function () { Route::get('/dashboard', [CustomerController::class, 'index'])->middleware(['auth', 'verified'])->name('dashboard'); Route::get('/customer/{id}', [CustomerController::class, 'show'])->middleware(['auth', 'verified']); +Route::get('/customer-groups', [CustomerGroupController::class, 'index'])->middleware(['auth', 'verified'])->name('customer-groups'); Route::get('/form', function () { return Inertia::render('Form');