authorize('viewAny', Letter::class); $query = Letter::with(['project', 'attachments']); if ($request->filled('type')) { $query->byType($request->type); } if ($request->filled('status')) { $query->byStatus($request->status); } if ($request->filled('project_id')) { $query->where('project_id', $request->project_id); } if ($request->filled('search')) { $query->search($request->search); } $letters = $query->orderBy('letter_date', 'desc') ->orderBy('created_at', 'desc') ->paginate(20) ->appends($request->query()); $projects = Project::all(); $types = ['incoming', 'outgoing']; $statuses = ['draft', 'sent', 'received', 'archived']; return view('letters.index', compact('letters', 'projects', 'types', 'statuses')); } /** * Show the form for creating a new letter. */ public function create(Request $request) { $this->authorize('create', Letter::class); $projects = Project::all(); $letters = Letter::orderBy('letter_date', 'desc')->limit(50)->get(); $letter = null; // Pre-fill parent_letter_id if provided $preselectedParent = $request->get('parent_letter_id'); return view('letters.form', compact('projects', 'letters', 'letter', 'preselectedParent')); } /** * Store a newly created letter in storage. */ public function store(Request $request) { $this->authorize('create', Letter::class); $validated = $request->validate([ 'type' => 'required|in:incoming,outgoing', 'subject' => 'required|string|max:255', 'sender' => 'required|string|max:255', 'recipient' => 'required|string|max:255', 'letter_date' => 'required|date', 'reference_number' => 'nullable|string|max:100', 'project_id' => 'nullable|exists:projects,id', 'parent_letter_id' => 'nullable|exists:letters,id', 'status' => 'required|in:draft,sent,received,archived', 'content' => 'nullable|string', 'attachments.*' => 'nullable|file|mimes:jpg,jpeg,png,pdf|max:10240', ]); // Convert jalali date if needed if (!empty($validated['letter_date'])) { $validated['letter_date'] = \App\Helpers\CalendarHelper::parseDate($validated['letter_date']); } $validated['tenant_id'] = $this->getTenantId(); $letter = Letter::create($validated); // Handle file attachments if ($request->hasFile('attachments')) { foreach ($request->file('attachments') as $file) { $this->storeAttachment($letter, $file); } } // Remove checked attachments (for edit, but safe to check) if ($request->has('remove_attachments')) { foreach ($request->remove_attachments as $attachmentId) { $this->removeAttachment($attachmentId, $letter); } } return redirect() ->route('letters.show', $letter) ->with('success', __('letter.created_successfully')); } /** * Display the specified letter. */ public function show(Letter $letter) { $this->authorize('view', $letter); $letter->load(['project', 'parentLetter', 'replies', 'attachments']); return view('letters.show', compact('letter')); } /** * Show the form for editing the specified letter. */ public function edit(Letter $letter) { $this->authorize('update', $letter); $letter->load('attachments'); $projects = Project::all(); $letters = Letter::where('id', '!=', $letter->id) ->orderBy('letter_date', 'desc') ->limit(50) ->get(); return view('letters.form', compact('letter', 'projects', 'letters')); } /** * Update the specified letter in storage. */ public function update(Request $request, Letter $letter) { $this->authorize('update', $letter); $validated = $request->validate([ 'type' => 'required|in:incoming,outgoing', 'subject' => 'required|string|max:255', 'sender' => 'required|string|max:255', 'recipient' => 'required|string|max:255', 'letter_date' => 'required|date', 'reference_number' => 'nullable|string|max:100', 'project_id' => 'nullable|exists:projects,id', 'parent_letter_id' => 'nullable|exists:letters,id', 'status' => 'required|in:draft,sent,received,archived', 'content' => 'nullable|string', 'attachments.*' => 'nullable|file|mimes:jpg,jpeg,png,pdf|max:10240', ]); // Convert jalali date if needed if (!empty($validated['letter_date'])) { $validated['letter_date'] = \App\Helpers\CalendarHelper::parseDate($validated['letter_date']); } $letter->update($validated); // Handle file attachments if ($request->hasFile('attachments')) { foreach ($request->file('attachments') as $file) { $this->storeAttachment($letter, $file); } } // Remove checked attachments if ($request->has('remove_attachments')) { foreach ($request->remove_attachments as $attachmentId) { $this->removeAttachment($attachmentId, $letter); } } return redirect() ->route('letters.show', $letter) ->with('success', __('letter.updated_successfully')); } /** * Remove the specified letter from storage. */ public function destroy(Letter $letter) { $this->authorize('delete', $letter); // Delete attachments from storage foreach ($letter->attachments as $attachment) { Storage::disk('public')->delete($attachment->file_path); } $letter->delete(); return redirect() ->route('letters.index') ->with('success', __('letter.deleted_successfully')); } /** * Archive the specified letter. */ public function archive(Letter $letter) { $this->authorize('update', $letter); $letter->update(['status' => 'archived']); return response()->json([ 'success' => true, 'message' => __('letter.archived_successfully'), ]); } /** * Store a file attachment for a letter. */ private function storeAttachment(Letter $letter, $file): void { $path = $file->store('letters/' . $letter->id, 'public'); LetterAttachment::create([ 'letter_id' => $letter->id, 'file_name' => $file->getClientOriginalName(), 'file_path' => $path, 'file_size' => $file->getSize(), 'mime_type' => $file->getMimeType(), ]); } /** * Remove a letter attachment. */ private function removeAttachment(int $attachmentId, Letter $letter): void { $attachment = LetterAttachment::where('id', $attachmentId) ->where('letter_id', $letter->id) ->first(); if ($attachment) { Storage::disk('public')->delete($attachment->file_path); $attachment->delete(); } } }