update
This commit is contained in:
parent
9b630fe1e1
commit
8cc13276f0
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
||||
|
|
@ -18,7 +18,7 @@ class Admin extends Authenticatable implements JWTSubject
|
|||
|
||||
public function __construct()
|
||||
{
|
||||
$this->table = 'admin';
|
||||
$this->table = 'users';
|
||||
$this->guarded = [];
|
||||
$this->hidden = [
|
||||
'password',
|
||||
|
|
|
|||
|
|
@ -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');
|
||||
}
|
||||
};
|
||||
|
|
@ -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'),
|
||||
],
|
||||
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue