This commit is contained in:
JOSEPH LE 2024-05-29 10:40:59 +07:00
parent 9b630fe1e1
commit 8cc13276f0
10 changed files with 488 additions and 1014 deletions

View File

@ -1,45 +0,0 @@
<?php
namespace Modules\Admin\app\Http\Controllers;
use App\Http\Controllers\Controller;
use App\Models\Contact;
use App\Traits\HasFilterRequest;
use App\Traits\HasOrderByRequest;
use App\Traits\HasSearchRequest;
use Illuminate\Http\Request;
use Modules\Admin\app\Http\Requests\ContactRequest;
class ContactController extends Controller
{
use HasOrderByRequest;
use HasFilterRequest;
use HasSearchRequest;
public function get(ContactRequest $request)
{
$contact = new Contact;
// Order by
$this->orderByRequest($contact, $request);
$this->searchRequest(
builder: $contact,
value: $request->get('search'),
fields: [
'name',
'phone',
'company',
'email',
]
);
$responseData = array_merge(
$contact->paginate($request->get('per_page'))->toArray(),
['status' => true]
);
return response()->json($responseData);
}
}

View File

@ -1,133 +0,0 @@
<?php
namespace Modules\Admin\app\Http\Controllers;
use App\Http\Controllers\Controller;
use App\Traits\HasFilterRequest;
use App\Traits\HasOrderByRequest;
use App\Traits\HasSearchRequest;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Modules\Admin\app\Http\Requests\DiscountRequest;
use Modules\Admin\app\Models\Discount;
class DiscountController extends Controller
{
use HasOrderByRequest;
use HasFilterRequest;
use HasSearchRequest;
public function get(DiscountRequest $request)
{
$discount = new Discount;
// Order by
$this->orderByRequest($discount, $request);
// Filter
$this->filterRequest(
builder: $discount,
request: $request,
filterKeys: [
'active_date' => self::F_THAN_EQ_DATETIME,
'expiry' => self::F_LESS_EQ_DATETIME,
'date_used' => self::F_IN_DATETIME,
'code' => self::F_TEXT,
'value' => self::F_TEXT,
'email' => self::F_TEXT,
'discount_type_id' => self::F_NOT_CONTAIN,
'status' => self::F_BOOLEAN,
]
);
$this->searchRequest(
builder: $discount,
value: $request->get('search'),
fields: [
'code',
'email',
'value'
]
);
$responseData = array_merge(
$discount->paginate($request->get('per_page'))->toArray(),
['status' => true]
);
return response()->json($responseData);
}
public function create(DiscountRequest $request)
{
$payload = $request->all();
$discount = Discount::createWithDefault($payload);
return response()->json([
'data' => $discount,
'status' => true
]);
}
public function update(DiscountRequest $request)
{
$id = $request->get('id');
$discount = Discount::find($id);
$payload = $request->all();
if ($discount) {
$discount->update($payload);
}
return response()->json([
'data' => $discount,
'status' => true
]);
}
public function delete(DiscountRequest $request)
{
$id = $request->get('id');
Discount::destroy($id);
return response()->json([
'status' => true
]);
}
// Delete multiple discounts
public function deletes(DiscountRequest $request)
{
$discounts = $request->get('discounts');
$ids = collect($discounts)->pluck('id');
Discount::whereIn('id', $ids)->delete();
return response()->json([
'data' => $ids,
'status' => true
]);
}
// Update multiple discounts
public function updates(DiscountRequest $request)
{
$discounts = $request->get('discounts');
$ids = collect($discounts)->pluck('id');
foreach ($discounts as $discountRequest) {
// convert to object|array to array
$discountRequest = collect($discountRequest)->toArray();
// handle array
$discount = Discount::find($discountRequest['id']);
if ($discount) {
// exclude id field
unset($discount['id']);
$discount->update($discountRequest);
}
}
return response()->json([
'data' => Discount::whereIn('id', $ids)->get(),
'status' => true
]);
}
}

View File

@ -1,20 +0,0 @@
<?php
namespace Modules\Admin\app\Http\Controllers;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Modules\Admin\app\Http\Requests\DiscountTypeRequest;
use Modules\Admin\app\Models\DiscountType;
class DiscountTypeController extends Controller
{
public function all(DiscountTypeRequest $request)
{
return response()->json([
'data' => DiscountType::all(),
'status' => true
]);
}
}

View File

@ -1,156 +0,0 @@
<?php
namespace Modules\Admin\app\Http\Controllers;
use App\Events\PaymentCanceled;
use App\Events\PaymentCompleted;
use App\Http\Controllers\Controller;
use App\Models\Client;
use App\Models\Order;
use App\Models\Package;
use App\Traits\HasFilterRequest;
use App\Traits\HasOrderByRequest;
use Illuminate\Http\Request;
use JsonException;
use Modules\Admin\app\Http\Requests\OrderRequest;
use Modules\Admin\app\Models\Discount;
use Modules\Paypal\app\Models\HistoryPayment;
class OrderController extends Controller
{
use HasOrderByRequest;
use HasFilterRequest;
const ORDER_STATUS_COMPLETE = 'COMPLETED';
const ORDER_STATUS_PENDING = 'PENDING';
const ORDER_STATUS_CANCEL = 'CANCELED';
public function __construct()
{
// module check model exist and table
if (!class_exists(Order::class)) {
throw new JsonException("Order table not exist" );
}
;
}
public function get(Request $request)
{
$order = Order::getOrdersWithDiscountAndPackage();
// Order by
$this->orderByRequest($order, $request);
// Filter
$this->filterRequest(
builder: $order,
request: $request,
filterKeys: [
'payment_id' => self::F_TEXT,
'discount' => self::F_TEXT,
'email' => [
'type' => self::F_TEXT,
'column' => 'users.email'
],
'status' => [
'type' => self::F_NOT_CONTAIN,
'column' => 'orders.status'
],
'from_date' => [
'type' => self::F_THAN_EQ_DATETIME,
'column' => 'orders.created_at'
],
'to_date' => [
'type' => self::F_LESS_EQ_DATETIME,
'column' => 'orders.created_at'
],
]
);
$responseData = array_merge(
$order->paginate($request->get('per_page'))->toArray(),
['status' => true]
);
return response()->json($responseData);
}
public function update(OrderRequest $request)
{
$id = $request->input('id');
$order = Order::find($id);
$client = Client::find($order['user_id']);
$package = Package::find($order['package_id']);
$order['status'] = $request->input('status');
if ($request->input('status') == self::ORDER_STATUS_COMPLETE) {
if ($order['discount']) {
// Update discount
$discount = Discount::where('code', $order['discount'])->first();
if ($discount != null) {
$discount['user_id'] = $client->id;
$discount['email'] = $client->email;
$discount['date_used'] = now();
$discount->save();
}
}
// Update point for client
$client->point += (int) $order['point'];
}
$status = -1;
if ($request->input('status') == self::ORDER_STATUS_COMPLETE) {
$status = 1;
event(
new PaymentCompleted(
payment_id: $order['payment_id'],
email: $client->email,
name: $client->name,
type: "PAYPAL",
product_name: $package->title,
point: $order->point,
discount_value: view_price((float) $package->price - (float) $order['total_price']),
total_price: $package->price
)
);
} elseif ($request->input('status') == self::ORDER_STATUS_CANCEL) {
$status = 0;
if ($order['discount']) {
$discount = Discount::where('code', $order['discount'])->first();
$discount['status'] = 1;
$discount->save();
}
event(
new PaymentCanceled(
payment_id: $order['payment_id'],
email: $client->email,
name: $client->name
)
);
} else {
$status = 2;
}
// Create history
HistoryPayment::create([
'payment_id' => $order['payment_id'],
'status' => $status,
'payload' => $request->all(),
'response' =>
[
'order' => $order,
'client' => $client,
'discount' => $discount ?? [],
'message' => "Change by Admin"
]
]);
$order->save();
$client->save();
return response()->json([
'data' => $order,
'status' => true
]);
}
}

View File

@ -1,94 +0,0 @@
<?php
namespace Modules\Admin\app\Http\Controllers;
use App\Helper\Cache\PackagesCacheHelper;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Modules\Admin\app\Http\Requests\PackageRequest;
use Modules\Admin\app\Models\Package;
class PackageController extends Controller
{
public function all(PackageRequest $request)
{
$package = new Package;
return response()->json([
'data' => $package->get(),
'status' => true,
]);
}
public function create(PackageRequest $request)
{
$package = Package::create($request->all());
PackagesCacheHelper::cleanCachePackages();
return response()->json([
'data' => $package,
'status' => true
]);
}
public function update(PackageRequest $request)
{
$id = $request->get('id');
$package = Package::find($id);
$package->update($request->all());
PackagesCacheHelper::cleanCachePackages();
return response()->json([
'data' => $package,
'status' => true
]);
}
public function delete(PackageRequest $request)
{
$id = $request->get('id');
Package::destroy($id);
PackagesCacheHelper::cleanCachePackages();
return response()->json([
'status' => true
]);
}
// Delete multiple packages
public function deletes(PackageRequest $request)
{
$packages = $request->get('packages');
$ids = collect($packages)->pluck('id');
Package::whereIn('id', $ids)->delete();
PackagesCacheHelper::cleanCachePackages();
return response()->json([
'data' => $ids,
'status' => true
]);
}
// Update multiple packages
public function updates(PackageRequest $request)
{
$packages = $request->get('packages');
$ids = collect($packages)->pluck('id');
foreach ($packages as $packageRequest) {
// convert to object|array to array
$packageRequest = collect($packageRequest)->toArray();
// handle array
$package = Package::find($packageRequest['id']);
if ($package) {
// exclude id field
unset($package['id']);
$package->update($packageRequest);
}
}
PackagesCacheHelper::cleanCachePackages();
return response()->json([
'data' => Package::whereIn('id', $ids)->get(),
'status' => true
]);
}
}

View File

@ -1,135 +0,0 @@
<?php
namespace Modules\Admin\app\Http\Controllers;
use App\Http\Controllers\Controller;
use App\Models\SerialNumberCheck;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use App\Traits\HasFilterRequest;
use App\Traits\HasOrderByRequest;
use JsonException;
class SNCheckController extends Controller
{
use HasOrderByRequest;
use HasFilterRequest;
public function __construct()
{
// module check model exist and table
if (!class_exists(SerialNumberCheck::class)) {
throw new JsonException("SerialNumberCheck table not exist");
}
;
}
public function get(Request $request)
{
$history = SerialNumberCheck::getHistoryWithUserInfo();
// Order by
$this->orderByRequest($history, $request);
// Filter
$this->filterRequest(
builder: $history,
request: $request,
filterKeys: [
'keyword' => [
'type' => self::F_TEXT,
'column' => 'serial_number_check.keyword'
],
'email' => [
'type' => self::F_TEXT,
'column' => 'users.email'
],
'status' => [
'type' => self::F_NOT_CONTAIN,
'column' => 'serial_number_check.status'
],
'from_date' => [
'type' => self::F_THAN_EQ_DATETIME,
'column' => 'serial_number_check.created_at'
],
'to_date' => [
'type' => self::F_LESS_EQ_DATETIME,
'column' => 'serial_number_check.created_at'
],
]
);
$responseData = array_merge(
$history->paginate($request->get('per_page'))->toArray(),
['status' => true]
);
return response()->json($responseData);
}
public function showDetail(Request $request)
{
$id = $request->input('id');
$searchRow = SerialNumberCheck::find($id);
if ($searchRow) {
$user = $searchRow->user()->get();
$tracking = $searchRow->tracking()->get();
$trackingDetail = $tracking->map(function ($item) {
return [
'current_point' => $item->current_point,
'use_point' => $item->use_point,
'created_at' => $item->created_at
];
});
$response = [
'data_search' => $searchRow,
'user' => $user[0],
'tracking' => $trackingDetail
];
return response()->json(['data' => $response, 'status' => true]);
}else{
return response()->json(['data' => [], 'status' => false]);
}
}
/**
* Store a newly created resource in storage.
*/
public function store(Request $request): RedirectResponse
{
//
}
/**
* Show the specified resource.
*/
public function show($id)
{
return view('admin::show');
}
/**
* Show the form for editing the specified resource.
*/
public function edit($id)
{
return view('admin::edit');
}
/**
* Update the specified resource in storage.
*/
public function update(Request $request, $id): RedirectResponse
{
//
}
/**
* Remove the specified resource from storage.
*/
public function destroy($id)
{
//
}
}

View File

@ -18,7 +18,7 @@ class Admin extends Authenticatable implements JWTSubject
public function __construct()
{
$this->table = 'admin';
$this->table = 'users';
$this->guarded = [];
$this->hidden = [
'password',

View File

@ -1,108 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('admin', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('email')->unique();
$table->timestamp('email_verified_at')->nullable();
$table->string('forgot_code')->nullable();
$table->string('password');
$table->rememberToken();
$table->timestamps();
});
DB::table('admin')->insert([
[
'name' => 'BIEN CONG NHUT TRUONG',
'email' => 'roger.b@apactech.io',
'password' => bcrypt('Work1234'),
],
[
'name' => 'PHAN QUOC BAO',
'email' => 'ryder.ph@apactech.io',
'password' => bcrypt('Work1234'),
],
[
'name' => 'PHAM VAN HUYNH',
'email' => 'jon.ph@apactech.io',
'password' => bcrypt('Work1234'),
],
[
'name' => 'VO MINH TRUONG',
'email' => 'vincent.vo@apactech.io',
'password' => bcrypt('Work1234'),
],
[
'name' => 'NGUYEN HUU PHUC',
'email' => 'jason.ng@apactech.io',
'password' => bcrypt('Work1234'),
],
[
'name' => 'NGUYEN VO TINH',
'email' => 'alex.ng@apactech.io',
'password' => bcrypt('Work1234'),
],
[
'name' => 'DANG TRUNG KIEN',
'email' => 'kevin.dang@apactech.io',
'password' => bcrypt('Work1234'),
],
[
'name' => 'HUYNH THI HONG GAM',
'email' => 'rose.h@apactech.io',
'password' => bcrypt('Work1234'),
],
[
'name' => 'NGUYEN TRUNG THAT',
'email' => 'andrew.ng@apactech.io',
'password' => bcrypt('Work1234'),
],
[
'name' => 'LE TAN LUAN',
'email' => 'joseph.le@apactech.io',
'password' => bcrypt('Work1234'),
],
[
'name' => 'VO VAN MINH',
'email' => 'michael.vo@apactech.io',
'password' => bcrypt('Work1234'),
],
[
'name' => 'TON GIA KHANH',
'email' => 'kai.t@apactech.io',
'password' => bcrypt('Work1234'),
],
[
'name' => 'PHAM QUOC HY',
'email' => 'asher.ph@apactech.io',
'password' => bcrypt('Work1234'),
],
[
'name' => 'Admin',
'email' => 'admin@apactech.io',
'password' => bcrypt('Work1234'),
],
]);
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('admin');
}
};

View File

@ -2,6 +2,7 @@
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
@ -16,10 +17,86 @@ return new class extends Migration
$table->string('name');
$table->string('email')->unique();
$table->timestamp('email_verified_at')->nullable();
$table->string('forgot_code')->nullable();
$table->string('password');
$table->string('permission')->default('staff');
$table->rememberToken();
$table->timestamps();
});
DB::table('users')->insert([
[
'name' => 'BIEN CONG NHUT TRUONG',
'email' => 'roger.b@apactech.io',
'password' => bcrypt('Work1234'),
],
[
'name' => 'PHAN QUOC BAO',
'email' => 'ryder.ph@apactech.io',
'password' => bcrypt('Work1234'),
],
[
'name' => 'PHAM VAN HUYNH',
'email' => 'jon.ph@apactech.io',
'password' => bcrypt('Work1234'),
],
[
'name' => 'VO MINH TRUONG',
'email' => 'vincent.vo@apactech.io',
'password' => bcrypt('Work1234'),
],
[
'name' => 'NGUYEN HUU PHUC',
'email' => 'jason.ng@apactech.io',
'password' => bcrypt('Work1234'),
],
[
'name' => 'NGUYEN VO TINH',
'email' => 'alex.ng@apactech.io',
'password' => bcrypt('Work1234'),
],
[
'name' => 'DANG TRUNG KIEN',
'email' => 'kevin.dang@apactech.io',
'password' => bcrypt('Work1234'),
],
[
'name' => 'HUYNH THI HONG GAM',
'email' => 'rose.h@apactech.io',
'password' => bcrypt('Work1234'),
],
[
'name' => 'NGUYEN TRUNG THAT',
'email' => 'andrew.ng@apactech.io',
'password' => bcrypt('Work1234'),
],
[
'name' => 'LE TAN LUAN',
'email' => 'joseph.le@apactech.io',
'password' => bcrypt('Work1234'),
],
[
'name' => 'VO VAN MINH',
'email' => 'michael.vo@apactech.io',
'password' => bcrypt('Work1234'),
],
[
'name' => 'TON GIA KHANH',
'email' => 'kai.t@apactech.io',
'password' => bcrypt('Work1234'),
],
[
'name' => 'PHAM QUOC HY',
'email' => 'asher.ph@apactech.io',
'password' => bcrypt('Work1234'),
],
[
'name' => 'Admin',
'email' => 'admin@apactech.io',
'password' => bcrypt('Work1234'),
],
]);
}
/**

View File

@ -1,6 +1,14 @@
import { getAllUserWorklogs } from '@/api/Admin'
import { get } from '@/rtk/helpers/apiService'
import { Avatar, Box, Button, Loader, Text } from '@mantine/core'
import {
Avatar,
Badge,
Box,
Button,
Loader,
Text,
Tooltip,
} from '@mantine/core'
import { DateInput } from '@mantine/dates'
import moment from 'moment'
import { useEffect, useState } from 'react'
@ -117,7 +125,7 @@ const Worklogs = () => {
startDate:
localStorage.getItem('data') !== null
? JSON.parse(localStorage.getItem('data')!).date.startDate
: moment(Date.now()-604800000).format('YYYY-MM-DD'),
: moment(Date.now() - 604800000).format('YYYY-MM-DD'),
endDate:
localStorage.getItem('data') !== null
? JSON.parse(localStorage.getItem('data')!).date.endDate
@ -172,361 +180,441 @@ const Worklogs = () => {
</Box>
) : (
<div style={{ display: 'flex', flexFlow: 'column' }}>
<div className={classes.title}>
<h3>
<Text>Admin/</Text>Worklogs
{!updating ? (
<Text fs={'italic'} fz={'xs'} c={'gray'}>
Updating data in the background ...
</Text>
) : (
''
)}
</h3>
</div>
<Box
display={'flex'}
style={{
flexFlow: 'column',
}}
>
<Text fz={12} fs={'italic'} c={'red'} fw={600} mt={'md'}>
{`* The current data is from ${JSON.parse(localStorage.getItem('data')!).date.startDate} to ${JSON.parse(localStorage.getItem('data')!).date.endDate}`}
</Text>
<Text fz={12} fs={'italic'} c={'red'} fw={600}>
{`* If you need data outside this time period, please select a date and click "Search" to update the latest data.`}
</Text>
<Box w={'100%'} display={'flex'} style={{ flexFlow: 'column' }}>
<Box
w={'100%'}
display={'flex'}
style={{
alignItems: 'end',
// justifyContent: 'space-between',
flexWrap: 'wrap',
}}
>
<DateInput
size="xs"
label="From date:"
value={new Date(date.startDate)}
w={'20%'}
clearable
onChange={(e) => {
setDate({ ...date, startDate: moment(e).format('YYYY-MM-DD') });
}}
/>
<DateInput
size="xs"
label="To date:"
value={new Date(date.endDate)}
clearable
m={"0 10px"}
w={'20%'}
onChange={(e) => {
setDate({ ...date, endDate: moment(e).format('YYYY-MM-DD') });
}}
/>
<Button
size="xs"
onClick={() => {
getAllWorklogs();
<div className={classes.title}>
<h3>
<Text>Admin/</Text>Worklogs
{!updating ? (
<Text fs={'italic'} fz={'xs'} c={'gray'}>
Updating data in the background ...
</Text>
) : (
''
)}
</h3>
</div>
<Box
display={'flex'}
style={{
flexFlow: 'column',
}}
>
<Text fz={12} fs={'italic'} c={'red'} fw={600} mt={'md'}>
{`* The current data is from ${
JSON.parse(localStorage.getItem('data')!).date.startDate
} to ${JSON.parse(localStorage.getItem('data')!).date.endDate}`}
</Text>
<Text fz={12} fs={'italic'} c={'red'} fw={600}>
{`* If you need data outside this time period, please select a date and click "Search" to update the latest data.`}
</Text>
<Box w={'100%'} display={'flex'} style={{ flexFlow: 'column' }}>
<Box
w={'100%'}
display={'flex'}
style={{
alignItems: 'end',
// justifyContent: 'space-between',
flexWrap: 'wrap',
}}
>
Search
</Button>
</Box>
</Box>
</Box>
{/* Box main content */}
<Box display={'flex'} mt={'lg'} style={{ flexWrap: 'wrap' }}>
<Box w={{ base: '100%', md: '15%' }} mb={'lg'}>
<Text fw={700} fz={14}>
Members
</Text>
<Box
style={{
border: 'solid 1px gray',
padding: '10px 20px',
marginRight: '10px',
borderRadius: '5px',
boxShadow: '1px 1px 5px 1px gray',
}}
>
{worklogs.map((w) => (
<Box
key={w.username}
style={{
display: 'flex',
alignItems: 'center',
fontSize: '13px',
<DateInput
size="xs"
label="From date:"
value={new Date(date.startDate)}
w={'20%'}
clearable
onChange={(e) => {
setDate({ ...date, startDate: moment(e).format('YYYY-MM-DD') })
}}
/>
<DateInput
size="xs"
label="To date:"
value={new Date(date.endDate)}
clearable
m={'0 10px'}
w={'20%'}
onChange={(e) => {
setDate({ ...date, endDate: moment(e).format('YYYY-MM-DD') })
}}
/>
<Button
size="xs"
onClick={() => {
getAllWorklogs()
}}
mb={'xs'}
className={classes.menuUser}
>
<Avatar src={w.user.avatarUrls['16x16']} size={'xs'} mr={5} />
<a
className={classes.menuLink}
href={'/worklogs#' + w.username}
style={{ textDecoration: 'none' }}
>
{w.username}
</a>
</Box>
))}
Search
</Button>
</Box>
</Box>
</Box>
<Box
w={{ base: '100%', md: '85%' }}
h={'85vh'}
display={'flex'}
style={{ overflowX: 'auto', flexFlow: 'column' }}
>
{worklogs?.map((user, index) => (
// Box user
{/* Box main content */}
<Box display={'flex'} mt={'lg'} style={{ flexWrap: 'wrap' }}>
<Box w={{ base: '100%', md: '15%' }} mb={'lg'}>
<Text fw={700} fz={14}>
Members
</Text>
<Box
id={user.username}
key={index}
p={'sm'}
style={{
border: 'solid 1px gray',
padding: '10px 20px',
marginRight: '10px',
borderRadius: '5px',
borderColor: '#afafaf',
marginBottom: '10px',
backgroundColor: index % 2 === 0 ? 'rgb(201 201 201 / 28%)' : 'white',
boxShadow: '1px 1px 5px 1px gray',
}}
>
<Text ta={'left'} mb={'xs'} fw={800} display={'flex'}>
<Avatar src={user.user.avatarUrls['48x48']} size={'sm'} m={'0 5px'} />
{user.username}(
{user?.information.issues.reduce((total, issue) => {
const totalSpent = issue.fields.worklog.worklogs?.reduce(
(accumulator, currentValue) => {
if (
parseInt(moment(date.startDate).format('YYYYMMDD')) <=
parseInt(moment(currentValue.started).format('YYYYMMDD')) &&
parseInt(moment(currentValue.started).format('YYYYMMDD')) <=
parseInt(moment(date.endDate).format('YYYYMMDD')) &&
currentValue.updateAuthor.displayName === user.username
) {
return accumulator + currentValue.timeSpentSeconds;
}
return accumulator;
},
0,
);
return total + totalSpent;
}, 0) /
60 /
60}
h)
</Text>
{/* Box issue-todo */}
<Box display={'flex'} style={{ justifyContent: 'space-between', flexWrap: 'wrap' }}>
{/* Box issue */}
{worklogs.map((w) => (
<Box
w={{ base: '100%', md: '50%' }}
key={w.username}
style={{
border: 'solid 1px gray',
borderRadius: '5px',
padding: '10px',
marginBottom: '5px',
overflowX: 'hidden',
color: '#412d2d',
backgroundColor: '#d1cdce',
display: 'flex',
alignItems: 'center',
fontSize: '13px',
}}
mb={'xs'}
className={classes.menuUser}
>
<Text fw={700} ta={'center'} mb={'5px'}>
WORKLOG
</Text>
{user.information.issues.map((iss) => {
if (
iss.fields.worklog.worklogs.filter(
(w) =>
<Avatar src={w.user.avatarUrls['16x16']} size={'xs'} mr={5} />
<a
className={classes.menuLink}
href={'/worklogs#' + w.username}
style={{ textDecoration: 'none' }}
>
{w.username}
</a>
</Box>
))}
</Box>
</Box>
<Box
w={{ base: '100%', md: '85%' }}
h={'85vh'}
display={'flex'}
style={{ overflowX: 'auto', flexFlow: 'column' }}
>
{worklogs?.map((user, index) => (
// Box user
<Box
id={user.username}
key={index}
p={'sm'}
style={{
border: 'solid 1px gray',
borderRadius: '5px',
borderColor: '#afafaf',
marginBottom: '10px',
backgroundColor:
index % 2 === 0 ? 'rgb(201 201 201 / 28%)' : 'white',
}}
>
<Text ta={'left'} mb={'xs'} fw={800} display={'flex'}>
<Avatar
src={user.user.avatarUrls['48x48']}
size={'sm'}
m={'0 5px'}
/>
{user.username}(
{user?.information.issues.reduce((total, issue) => {
const totalSpent = issue.fields.worklog.worklogs?.reduce(
(accumulator, currentValue) => {
if (
parseInt(moment(date.startDate).format('YYYYMMDD')) <=
parseInt(moment(w.started).format('YYYYMMDD')) &&
parseInt(moment(date.endDate).format('YYYYMMDD')) >=
parseInt(moment(w.started).format('YYYYMMDD')) &&
w.updateAuthor.displayName === user.username,
).length > 0
) {
parseInt(
moment(currentValue.started).format('YYYYMMDD'),
) &&
parseInt(
moment(currentValue.started).format('YYYYMMDD'),
) <=
parseInt(moment(date.endDate).format('YYYYMMDD')) &&
currentValue.updateAuthor.displayName === user.username
) {
return accumulator + currentValue.timeSpentSeconds
}
return accumulator
},
0,
)
return total + totalSpent
}, 0) /
60 /
60}
h)
</Text>
{/* Box issue-todo */}
<Box
display={'flex'}
style={{ justifyContent: 'space-between', flexWrap: 'wrap' }}
>
{/* Box issue */}
<Box
w={{ base: '100%', md: '50%' }}
style={{
border: 'solid 1px gray',
borderRadius: '5px',
padding: '10px',
marginBottom: '5px',
overflowX: 'hidden',
color: '#412d2d',
backgroundColor: '#d1cdce',
}}
>
<Text fw={700} ta={'center'} mb={'5px'}>
WORKLOG
</Text>
{user.information.issues.map((iss) => {
if (
iss.fields.worklog.worklogs.filter(
(w) =>
parseInt(moment(date.startDate).format('YYYYMMDD')) <=
parseInt(moment(w.started).format('YYYYMMDD')) &&
parseInt(moment(date.endDate).format('YYYYMMDD')) >=
parseInt(moment(w.started).format('YYYYMMDD')) &&
w.updateAuthor.displayName === user.username,
).length > 0
) {
return (
<Box
key={iss.id}
style={{
border: 'solid 1px gray',
borderRadius: '5px',
boxShadow: '1px 1px 5px 1px gray',
padding: '10px',
marginBottom: '8px',
maxHeight: '90vh',
overflowX: 'hidden',
color: '#412d2d',
backgroundColor: 'white',
}}
>
{/* Box information issue */}
<Box>
<Text
fz={14}
c={iss.fields.status.statusCategory.colorName}
>
<b>{iss.fields.status.name}</b>
</Text>
<Badge color="#6140c0e0">
<Box
display={'flex'}
style={{ alignItems: 'center' }}
>
<Avatar
src={iss.fields.project?.avatarUrls['16x16']}
size={'xs'}
m={'0 5px 0 0'}
/>
{iss.fields.project.name}
</Box>
</Badge>
<Text
fz={14}
display={'flex'}
style={{ alignItems: 'start' }}
>
<b>Summary:</b>{' '}
<Tooltip label={iss.fields.summary}>
<Badge color="#228be64f" fz={12}>
<a
href={`https://apactechvn.atlassian.net/browse/${iss.key}`}
target="_blank"
style={{textDecoration:"none"}}
>
[{iss.key}]{` ${iss.fields.summary}`}
</a>
</Badge>
</Tooltip>
</Text>
<Text fz={14}>
<b>Estimate:</b>{' '}
<Badge color='red' size='sm'>{iss.fields.timeoriginalestimate / 60 / 60}h</Badge>
</Text>
<Text fz={14}>
<b>Total time spent:</b>{' '}
<Badge color='orange' size='sm'>{iss.fields.timespent / 60 / 60}h</Badge>
</Text>
<Text fz={14}>
<b>
Time spent{' '}
<span style={{ fontSize: '11px' }}>
(
{date.startDate === date.endDate
? date.startDate
: date.startDate + ' to ' + date.endDate}
)
</span>
:
</b>{' '}<Badge color='green' size='sm' mb={'xs'}>
{iss.fields.worklog.worklogs?.reduce(
(accumulator, currentValue) => {
if (
parseInt(
moment(date.startDate).format('YYYYMMDD'),
) <=
parseInt(
moment(currentValue.started).format(
'YYYYMMDD',
),
) &&
parseInt(
moment(currentValue.started).format(
'YYYYMMDD',
),
) <=
parseInt(
moment(date.endDate).format('YYYYMMDD'),
) &&
currentValue.updateAuthor.displayName ===
user.username
) {
return (
accumulator +
currentValue.timeSpentSeconds
)
}
return accumulator
},
0,
) /
60 /
60}
h</Badge>
</Text>
</Box>
{iss.fields.worklog.worklogs?.map((log, index) => {
if (
moment(date.startDate).format('YYYYMMDD') <=
moment(log.started).format('YYYYMMDD') &&
moment(log.started).format('YYYYMMDD') <=
moment(date.endDate).format('YYYYMMDD') &&
log.updateAuthor.displayName === user.username
) {
return (
// Box worklog
<Box
key={index}
style={{
padding: '4px 8px',
marginBottom: '5px',
marginLeft: '10px',
backgroundColor: '#d9d9d9',
}}
>
<Text fz={13}>
<b>Start date:</b>{' '}
{moment(log.started).format(
'HH:mm YYYY/MM/DD',
)}
</Text>
<Text fz={13}>
<b>Time spent:</b> {log.timeSpent}
</Text>
{log?.comment &&
log?.comment?.content[0]?.content[0]
?.text && (
<Text fz={13}>
<b>Comment:</b>{' '}
{
log?.comment?.content[0]?.content[0]
?.text
}
</Text>
)}
</Box>
)
}
})}
</Box>
)
}
})}
</Box>
{/* Box todo */}
<Box
w={{ base: '100%', md: '49%' }}
style={{
border: 'solid 1px gray',
borderRadius: '5px',
padding: '10px',
marginBottom: '5px',
overflowX: 'hidden',
color: '#412d2d',
backgroundColor: '#f9ffa47a',
}}
>
<Text fw={700} ta={'center'} mb={'5px'}>
ASSIGNMENT
</Text>
{user.tasksAssign.issues?.map((iss, index) => {
return (
<Box
key={iss.id}
key={index}
style={{
border: 'solid 1px gray',
borderRadius: '5px',
boxShadow: '1px 1px 5px 1px gray',
padding: '10px',
marginBottom: '8px',
maxHeight: '90vh',
marginBottom: '5px',
overflowX: 'hidden',
color: '#412d2d',
backgroundColor: 'white',
}}
>
{/* Box information issue */}
<Box>
<Text fz={14} c={iss.fields.status.statusCategory.colorName}>
<b>{iss.fields.status.name}</b>
</Text>
<Text fz={14} display={'flex'} style={{ alignItems: 'start' }}>
<b>Summary:</b>{' '}
<Box display={'flex'} style={{ alignItems: 'center' }}>
<Avatar
src={iss.fields.project?.avatarUrls['16x16']}
size={'xs'}
m={'0 5px'}
/>
<Text fz={14} mr={'xs'}>
{iss.fields.project.name}
</Text>
</Box>
<a href={`https://apactechvn.atlassian.net/browse/${iss.key}`} target="_blank">
{iss.fields.summary}
</a>
</Text>
<Text fz={14}>
<b>Estimate:</b> {iss.fields.timeoriginalestimate / 60 / 60}h
</Text>
<Text fz={14}>
<b>Total time spent:</b> {iss.fields.timespent / 60 / 60}h
</Text>
<Text fz={14}>
<b>
Time spent{' '}
<span style={{ fontSize: '11px' }}>
(
{date.startDate === date.endDate
? date.startDate
: date.startDate + ' to ' + date.endDate}
)
</span>
:
</b>{' '}
{iss.fields.worklog.worklogs?.reduce(
(accumulator, currentValue) => {
if (
parseInt(moment(date.startDate).format('YYYYMMDD')) <=
parseInt(moment(currentValue.started).format('YYYYMMDD')) &&
parseInt(moment(currentValue.started).format('YYYYMMDD')) <=
parseInt(moment(date.endDate).format('YYYYMMDD')) &&
currentValue.updateAuthor.displayName === user.username
) {
return accumulator + currentValue.timeSpentSeconds;
}
return accumulator;
},
0,
) /
60 /
60}
h
</Text>
</Box>
{iss.fields.worklog.worklogs?.map((log, index) => {
if (
moment(date.startDate).format('YYYYMMDD') <= moment(log.started).format('YYYYMMDD') &&
moment(log.started).format('YYYYMMDD') <= moment(date.endDate).format('YYYYMMDD') &&
log.updateAuthor.displayName === user.username
) {
return (
// Box worklog
<Box
key={index}
style={{
padding: '4px 8px',
marginBottom: '5px',
marginLeft: '10px',
backgroundColor: '#d9d9d9',
}}
<Text
fz={14}
c={iss.fields.status.statusCategory.colorName}
>
<b>{iss.fields.status.name}</b>
</Text>
<Badge color="#6140c0e0">
<Box
display={'flex'}
style={{ alignItems: 'center' }}
>
<Avatar
src={iss.fields.project?.avatarUrls['16x16']}
size={'xs'}
m={'0 5px 0 0'}
/>
{iss.fields.project.name}
</Box>
</Badge>
<Text
fz={14}
display={'flex'}
style={{ alignItems: 'start' }}
>
<b>Summary:</b>{' '}
<Tooltip label={iss.fields.summary}>
<Badge color="#228be64f" fz={12}>
<a
href={`https://apactechvn.atlassian.net/browse/${iss.key}`}
target="_blank"
style={{textDecoration:"none"}}
>
<Text fz={13}>
<b>Start date:</b> {moment(log.started).format('HH:mm YYYY/MM/DD')}
</Text>
<Text fz={13}>
<b>Time spent:</b> {log.timeSpent}
</Text>
{log?.comment && log?.comment?.content[0]?.content[0]?.text && (
<Text fz={13}>
<b>Comment:</b> {log?.comment?.content[0]?.content[0]?.text}
</Text>
)}
</Box>
);
}
})}
</Box>
);
}
})}
</Box>
{/* Box todo */}
<Box
w={{ base: '100%', md: '49%' }}
style={{
border: 'solid 1px gray',
borderRadius: '5px',
padding: '10px',
marginBottom: '5px',
overflowX: 'hidden',
color: '#412d2d',
backgroundColor: '#f9ffa47a',
}}
>
<Text fw={700} ta={'center'} mb={'5px'}>
ASSIGNMENT
</Text>
{user.tasksAssign.issues?.map((iss, index) => {
return (
<Box
key={index}
style={{
border: 'solid 1px gray',
borderRadius: '5px',
boxShadow: '1px 1px 5px 1px gray',
padding: '10px',
marginBottom: '5px',
overflowX: 'hidden',
color: '#412d2d',
backgroundColor: 'white',
}}
>
<Box display={'flex'} style={{ alignItems: 'center' }}>
<Avatar
src={iss.fields.project?.avatarUrls['16x16']}
size={'xs'}
m={'0 5px 0 0'}
/>
<Text fz={14} mr={'xs'}>
{iss.fields.project.name}
[{iss.key}]{` ${iss.fields.summary}`}
</a>
</Badge>
</Tooltip>
</Text>
<Text fz={13}>
<b>Time spent:</b> <Badge color='orange' size='sm'>{iss.fields.timespent / 60 / 60}h</Badge>
</Text>
<Text fz={13}>
<b>Estimate:</b>{' '}
<Badge color='red' size='sm'>{iss.fields.timeoriginalestimate / 60 / 60}h</Badge>
</Text>
</Box>
<Text fz={14} display={'flex'} style={{ alignItems: 'start' }}>
<b style={{ marginRight: '5px' }}>Summary:</b>
<a href={`https://apactechvn.atlassian.net/browse/${iss.key}`} target="_blank">
{iss.fields.summary}
</a>
</Text>
<Text fz={13}>
<b>Time spent:</b> {iss.fields.timespent / 60 / 60}h
</Text>
<Text fz={13}>
<b>Estimate:</b> {iss.fields.timeoriginalestimate / 60 / 60}h
</Text>
<Text fz={14} c={iss.fields.status.statusCategory.colorName}>
<b>{iss.fields.status.name}</b>
</Text>
</Box>
);
})}
)
})}
</Box>
</Box>
</Box>
</Box>
))}
))}
</Box>
</Box>
</Box>
</div>
</div>
)
}