twopenny
ajikamaludin 1 year ago
parent 4c8992022a
commit ecb336ffec
Signed by: ajikamaludin
GPG Key ID: 476C9A2B4B794EBB

@ -4,6 +4,10 @@ FROM php:8.1-fpm
ARG user
ARG uid
ENV TZ=Asia/Jakarta
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
# Install system dependencies
RUN apt-get update && apt-get install -y \
git \

@ -16,7 +16,6 @@ class Kernel extends ConsoleKernel
*/
protected function schedule(Schedule $schedule)
{
// $schedule->command('inspire')->hourly();
$schedule->job(new DocumentReminder)->daily();
}

@ -2,18 +2,10 @@
namespace App\Http\Controllers;
use App\Mail\DocumentShare;
use App\Models\Category;
use App\Models\Department;
use App\Models\Document;
use App\Models\Type;
use App\Models\TypeDoc;
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Mail;
use OpenSpout\Writer\Common\Creator\Style\StyleBuilder;
use Rap2hpoutre\FastExcel\FastExcel;
class DocumentController extends Controller
{
@ -38,6 +30,8 @@ class DocumentController extends Controller
return inertia('Document/Index', [
'docs' => $query->paginate(10),
'now' => now()->timezone('UTC'),
'now2' => now()->toDateTimeString(),
]);
}
@ -52,15 +46,15 @@ class DocumentController extends Controller
public function store(Request $request)
{
$request->validate([
"no_doc" => "required|string",
"no_doc" => "nullable|string",
"name" => "required|string",
"company_name" => "required|string",
"type_id" => "required|exists:types,id",
"category_id" => "required|exists:categories,id",
"publisher" => "required|string",
"description" => "nullable",
"publish_date" => "required|date",
"due_date" => "required_if:type,1",
"publish_date" => "nullable|date",
"due_date" => "required_if:type,0",
"status" => "required|in:0,1",
"type" => "required|in:0,1",
"group" => "required|string",
@ -68,12 +62,19 @@ class DocumentController extends Controller
"document" => "required|file",
]);
if($request->publish_date == Document::TYPE_TETAP) {
if($request->type == Document::TYPE_TIDAK_TETAP) {
$request->validate([
"due_date" => "date|after_or_equal:".$request->publish_date
]);
}
if($request->status == Document::STATUS_YES) {
$request->validate([
"no_doc" => "required|string",
"publish_date" => "required|date",
]);
}
$doc = Document::make([
"no" => Document::count() + 1,
"no_doc" => $request->no_doc,
@ -114,14 +115,14 @@ class DocumentController extends Controller
public function update(Request $request, Document $doc)
{
$request->validate([
"no_doc" => "required|string",
"no_doc" => "nullable|string",
"name" => "required|string",
"company_name" => "required|string",
"type_id" => "required|exists:types,id",
"category_id" => "required|exists:categories,id",
"publisher" => "required|string",
"description" => "nullable",
"publish_date" => "required|date",
"publish_date" => "nullable|date",
"due_date" => "required_if:type,1",
"status" => "required|in:0,1",
"type" => "required|in:0,1",
@ -130,12 +131,19 @@ class DocumentController extends Controller
"document" => "nullable|file",
]);
if($request->publish_date == Document::TYPE_TETAP) {
if($request->type == Document::TYPE_TETAP) {
$request->validate([
"due_date" => "date|after_or_equal:".$request->publish_date
]);
}
if($request->status == Document::STATUS_YES) {
$request->validate([
"no_doc" => "required|string",
"publish_date" => "required|date",
]);
}
$doc->fill([
"no" => Document::count() + 1,
"no_doc" => $request->no_doc,

@ -2,20 +2,26 @@
namespace App\Http\Controllers;
use App\Models\Category;
use App\Models\Document;
use App\Models\DocumentReminder;
use Illuminate\Http\Request;
class GeneralController extends Controller
{
public function index()
{
$count = 0;
$categories = Category::all();
foreach($categories as $category) {
foreach($category->documents as $docs) {
if ($docs->is_close_due != 0) {
$count += 1;
}
}
}
return inertia('Dashboard', [
'count_active' => 0,
'count_update' => 0,
'count_expired' => 0,
'count_total' => 0,
'events' => DocumentReminder::with('document.type')->get(),
'count_active' => $count,
'count_update' => Document::whereDate('due_date', '<', now()->toDateString())->count(),
]);
}
}

@ -2,17 +2,13 @@
namespace App\Jobs;
use App\Models\Document;
use App\Mail\DocumentNotification;
use App\Models\DocumentReminder as ModelsDocumentReminder;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Log\Logger;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Mail;
class DocumentReminder implements ShouldQueue
@ -36,19 +32,6 @@ class DocumentReminder implements ShouldQueue
*/
public function handle()
{
$now = now();
$documentIds = ModelsDocumentReminder::whereDate('date', $now)->pluck('document_id');
Log::info("DocumentReminder:: Schedule Run $now , with $documentIds");
$documents = Document::whereIn('id', $documentIds)->get();
foreach ($documents as $doc) {
Mail::to($doc->email)->send(new DocumentNotification($doc));
if ($doc->shares()->count() > 0) {
foreach ($doc->shares as $share) {
Mail::to($share->share_to)->send(new DocumentNotification($doc));
}
}
}
Mail::to('aji19kamaludin@gmail.com')->send(new DocumentNotification());
}
}

@ -2,6 +2,7 @@
namespace App\Mail;
use App\Models\Category;
use App\Models\Document;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
@ -17,10 +18,7 @@ class DocumentNotification extends Mailable
*
* @return void
*/
public function __construct(
public Document $doc
) {
}
public function __construct() {}
/**
* Build the message.
@ -29,9 +27,19 @@ class DocumentNotification extends Mailable
*/
public function build()
{
$docs = collect();
$categories = Category::all();
foreach($categories as $category) {
foreach($category->documents()->get() as $doc) {
if ($doc->is_close_due != 0) {
$docs->add($doc);
}
}
}
return $this->markdown('emails.document.notification', [
'doc' => $this->doc->load(['type']),
'url' => route('docs.show', $this->doc->id)
'documents' => $docs,
'dueDocuments' => Document::whereDate('due_date', '<', now()->toDateString())->get()
]);
}
}

@ -14,4 +14,9 @@ class Category extends Model
"short",
"duration",
];
public function documents()
{
return $this->hasMany(Document::class);
}
}

@ -2,8 +2,10 @@
namespace App\Models;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Carbon;
class Document extends Model
{
@ -39,6 +41,8 @@ class Document extends Model
'due_date' => 'datetime:Y-m-d'
];
protected $appends = ['due_status'];
public function creator()
{
return $this->belongsTo(User::class, 'user_id');
@ -53,4 +57,57 @@ class Document extends Model
{
return $this->belongsTo(Category::class, 'category_id');
}
protected function dueStatus(): Attribute
{
return Attribute::make(
get: function() {
$diff = $this->category->duration;
if ($this->due_date == null) {
return '';
}
if (now()->toDateString() == $this->due_date->toDateString()) {
return "hari ini jatuh tempo";
}
$date = now()->diffInDays($this->due_date, false) + 1;
if ($diff >= $date && $date > 0) {
return $date . " hari mendekati jatuh tempo";
}
if ($date <= 0) {
return "jatuh tempo";
}
},
);
}
protected function isCloseDue(): Attribute
{
return Attribute::make(
get: function() {
$diff = $this->category->duration;
if ($this->due_date == null) {
return 0;
}
if (now()->toDateString() == $this->due_date->toDateString()) {
return 0;
}
$date = now()->diffInDays($this->due_date, false) + 1;
if ($diff >= $date && $date > 0) {
return $date;
}
if ($date <= 0) {
return 0;
}
return 0;
},
);
}
}

@ -69,7 +69,7 @@ return [
|
*/
'timezone' => 'UTC',
'timezone' => 'Asia/Jakarta',
/*
|--------------------------------------------------------------------------

@ -13,6 +13,8 @@ services:
volumes:
- ./:/var/www
- ./user.ini:/usr/local/etc/php/php.ini
- "/etc/timezone:/etc/timezone:ro"
- "/etc/localtime:/etc/localtime:ro"
networks:
- monitordoc
nginx:

@ -11,14 +11,14 @@ import { router } from '@inertiajs/react'
export default function Dashboard(props) {
const { count_active, count_update, count_expired, count_total, events } = props
const calenderEvents = events.map(e => {
return {
title: `${e.document.type.name} - ${e.document.name}`,
date: e.date,
id : e.id,
url: route('docs.show', e.document)
}
})
// const calenderEvents = events.map(e => {
// return {
// title: `${e.document.type.name} - ${e.document.name}`,
// date: e.date,
// id : e.id,
// url: route('docs.show', e.document)
// }
// })
const handleEventClick = (arg) => {
// console.log(arg.event)
@ -40,19 +40,19 @@ export default function Dashboard(props) {
<Head title="Dashboard" />
<div className='mx-auto px-2 md:px-4 lg:px-8 grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-1'>
<div className="stats bg-base-100 shadow-md w-full">
<div className="stats bg-base-100 shadow-md w-full overflow-hidden">
<div className="stat">
<div className="stat-title">Dokumen Aktif</div>
<div className="stat-title">Dokumen Mendekati Jatuh Tempo</div>
<div className="stat-value">{count_active}</div>
</div>
</div>
<div className="stats bg-base-100 shadow-md w-full">
<div className="stats bg-base-100 shadow-md w-full overflow-hidden">
<div className="stat">
<div className="stat-title">Dokumen Diperbarui</div>
<div className="stat-title">Dokumen Jatuh Tempo</div>
<div className="stat-value">{count_update}</div>
</div>
</div>
<div className="stats bg-base-100 shadow-md w-full">
{/* <div className="stats bg-base-100 shadow-md w-full">
<div className="stat">
<div className="stat-title">Dokumen Berakhir</div>
<div className="stat-value">{count_expired}</div>
@ -63,9 +63,9 @@ export default function Dashboard(props) {
<div className="stat-title">Total Dokumen</div>
<div className="stat-value">{count_total}</div>
</div>
</div>
</div> */}
</div>
<div className='bg-base-100 mx-2 md:mx-8 mt-4 p-2 md:p-4 lg:p-8 h-auto'>
{/* <div className='bg-base-100 mx-2 md:mx-8 mt-4 p-2 md:p-4 lg:p-8 h-auto'>
<FullCalendar
plugins={[ dayGridPlugin, interactionPlugin ]}
initialView="dayGridMonth"
@ -73,7 +73,7 @@ export default function Dashboard(props) {
eventClick={handleEventClick}
events={calenderEvents}
/>
</div>
</div> */}
</AuthenticatedLayout>
);
}

@ -177,6 +177,17 @@ export default function FormDocument(props) {
</label>
</div>
</div>
<div className='mt-4'>
<InputLabel forInput="due_status" value="Catatan" />
<TextInput
type="text"
name="due_status"
value={doc.due_status}
className="mt-1 block w-full"
autoComplete={"false"}
readOnly={true}
/>
</div>
<div className="flex items-center justify-between mt-4">
<div className='flex flex-row space-x-1'>
<Link href={route('docs.edit', doc)} className="btn btn-outline">

@ -108,7 +108,7 @@ export default function Document(props) {
<th>No Dokumen</th>
<th>Nama Dokumen</th>
<th className='hover:underline' onClick={() => sort('due_date')}>Tanggal Berakhir</th>
<th>Status</th>
<th>Catatan</th>
<th></th>
</tr>
</thead>
@ -119,8 +119,10 @@ export default function Document(props) {
<td>{doc.category.name}</td>
<td>{doc.no_doc}</td>
<td>{doc.name}</td>
<td>{formatDate(doc.due_date)}</td>
<th>Status</th>
<td>
{doc.due_date !== null ? formatDate(doc.due_date) : ''}
</td>
<th>{doc.due_status}</th>
<td className='text-right'>
<div className="dropdown dropdown-left">
<label tabIndex={0} className="btn btn-sm m-1 px-1"><IconMenu/></label>

@ -1,18 +1,13 @@
@component('mail::message')
# Dokumen Notifikasi
Reminder, untuk dokumen dengan detail :
No Dokumen : {{ $doc->no_doc }} <br/>
Jenis Dokumen : {{ $doc->type->name }} <br/>
Nama Dokumen : {{ $doc->name }} <br/>
Nama Perusahaan : {{ $doc->company_name }} <br/>
Akan berakhir pada <b>{{ $doc->end_date->format('d-m-Y') }}</b> mohon untuk segera melakukan tindakan
@component('mail::button', ['url' => $url])
Detail Dokumen
@endcomponent
Reminder, untuk dokumen perlu diperhatikan :
@foreach($documents as $document)
{{ $document->no_doc }} {{ $document->name }} | {{ $document->due_status }}
@endforeach
@foreach($dueDocuments as $document)
{{ $document->no_doc }} {{ $document->name }} | {{ $document->due_status }}
@endforeach
Terima kasih , <br>
{{ config('app.name') }}

1975
user.ini

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save