Add the note templates feature

This commit is contained in:
Bruno
2024-12-22 14:00:36 +00:00
parent b572d06f83
commit 421c5134fa
7 changed files with 138 additions and 5 deletions

View File

@@ -57,12 +57,12 @@ class VaultNodeForm extends Form
$this->content = $node->content;
}
public function create(): void
public function create(): VaultNode
{
$this->validate();
$this->name = Str::trim($this->name);
$this->vault->nodes()->create([
$node = $this->vault->nodes()->create([
'parent_id' => $this->parent_id,
'is_file' => $this->is_file,
'name' => $this->name,
@@ -70,6 +70,8 @@ class VaultNodeForm extends Form
'content' => $this->content,
]);
$this->reset(['name']);
return $node;
}
public function update(): void

View File

@@ -33,9 +33,12 @@ class AddNode extends Modal
public function add(): void
{
$this->form->create();
$node = $this->form->create();
$this->closeModal();
$this->dispatch('node-updated');
if ($node->parent_id == $this->form->vault->templates_node_id) {
$this->dispatch('templates-refresh');
}
$message = $this->form->is_file ? __('File created') : __('Folder created');
$this->dispatch('toast', message: $message, type: 'success');
}

View File

@@ -10,12 +10,14 @@ use Livewire\Attributes\On;
use Livewire\Attributes\Url;
use App\Actions\ResolveTwoPaths;
use App\Livewire\Forms\VaultForm;
use App\Services\VaultFiles\Note;
use Illuminate\Support\Facades\DB;
use App\Actions\GetUrlFromVaultNode;
use App\Actions\GetPathFromVaultNode;
use App\Actions\GetVaultNodeFromPath;
use App\Livewire\Forms\VaultNodeForm;
use Illuminate\Support\Facades\Storage;
use Illuminate\Database\Eloquent\Collection;
class Show extends Component
{
@@ -25,6 +27,8 @@ class Show extends Component
public VaultNodeForm $nodeForm;
public Collection $templates;
#[Url(as: 'file')]
public ?int $selectedFile = null;
@@ -32,12 +36,15 @@ class Show extends Component
public bool $isEditMode = true;
private array $deletedNodes = [];
public function mount(Vault $vault): void
{
$this->authorize('view', $vault);
$this->vault = $vault;
$this->vaultForm->setVault($this->vault);
$this->nodeForm->setVault($this->vault);
$this->getTemplates();
if ($this->selectedFile) {
$this->openFile(VaultNode::find($this->selectedFile));
@@ -48,7 +55,7 @@ class Show extends Component
{
$this->authorize('view', $node->vault);
if ($node->vault != $this->vault || !$node->is_file) {
if (!$node->vault->is($this->vault) || !$node->is_file) {
return;
}
@@ -115,9 +122,68 @@ class Show extends Component
if ($this->nodeForm->node->wasChanged(['parent_id', 'name'])) {
$this->dispatch('node-updated');
if ($this->nodeForm->node->parent_id == $this->vault->templates_node_id) {
$this->getTemplates();
}
}
}
public function setTemplateFolder(VaultNode $node): void
{
$this->authorize('update', $node->vault);
if ($this->vault->id !== $node->vault->id || $node->is_file) {
$this->dispatch('toast', message: __('Something went wrong'), type: 'error');
return;
}
$this->vault->update(['templates_node_id' => $node->id]);
$this->getTemplates();
$this->dispatch('toast', message: __('Template folder updated'), type: 'success');
}
public function insertTemplate(VaultNode $node): void
{
$this->authorize('update', $this->vault);
$sameVault = $this->vault->id === $node->vault->id;
$isNote = $node->is_file && in_array($node->extension, Note::extensions());
$isTemplate = $node->parent_id == $this->vault->templates_node_id;
if (!$sameVault || !$isNote || !$isTemplate || !$this->selectedFile || !$this->isEditMode) {
$this->dispatch('toast', message: __('Something went wrong'), type: 'error');
return;
}
$selectedNode = $this->nodeForm->node;
$now = now();
$content = $node->content;
$content = Str::replace('{{date}}', $now->format('Y-m-d'), $content);
$content = Str::replace('{{time}}', $now->format('H:i'), $content);
$content = Str::contains($content, '{{content}}')
? Str::replace('{{content}}', $selectedNode->content, $content)
: $content . PHP_EOL . $selectedNode->content;
$selectedNode->update(['content' => $content]);
$this->nodeForm->setNode($selectedNode);
$this->dispatch('toast', message: __('Template inserted'), type: 'success');
}
#[On('templates-refresh')]
public function getTemplates(): void
{
if (!$this->vault->templatesNode) {
return;
}
$this->templates = $this->vault
->templatesNode
->childs()
->where('is_file', true)
->where('extension', 'LIKE', 'md')
->orderBy('name')
->get();
}
public function deleteNode(VaultNode $node): void
{
$this->authorize('delete', $node->vault);
@@ -132,6 +198,15 @@ class Show extends Component
DB::commit();
$this->dispatch('node-updated');
$templateDeleted = !is_null(
array_find($this->deletedNodes, function ($node) {
return $node->parent_id == $this->vault->templates_node_id;
})
);
if ($templateDeleted) {
$this->getTemplates();
}
$this->deletedNodes = [];
$message = $node->is_file ? __('File deleted') : __('Folder deleted');
$this->dispatch('toast', message: $message, type: 'success');
} catch (\Throwable $e) {
@@ -150,6 +225,7 @@ class Show extends Component
Storage::disk('local')->delete($relativePath);
}
$this->deletedNodes[] = $node;
$node->delete();
}
@@ -163,6 +239,7 @@ class Show extends Component
}
}
$this->deletedNodes[] = $node;
$node->delete();
}

View File

@@ -8,6 +8,7 @@ use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\HasOne;
class Vault extends Model
{
@@ -20,6 +21,7 @@ class Vault extends Model
* @var string[]
*/
protected $fillable = [
'templates_node_id',
'name',
];
@@ -38,4 +40,12 @@ class Vault extends Model
{
return $this->hasMany(VaultNode::class);
}
/**
* Get the associated templates node.
*/
public function templatesNode(): HasOne
{
return $this->hasOne(VaultNode::class, 'id', 'templates_node_id');
}
}

View File

@@ -0,0 +1,30 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('vaults', function (Blueprint $table) {
$table->after('name', function (Blueprint $table) {
$table->foreignId('templates_node_id')->nullable()->constrained('vault_nodes');
});
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('vaults', function (Blueprint $table) {
$table->dropColumn('templates_node_id');
});
}
};

View File

@@ -0,0 +1,6 @@
<svg {{ $attributes }} data-slot="icon" aria-hidden="true" fill="none" stroke-width="1.5" stroke="currentColor"
viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path
d="M15.75 17.25v3.375c0 .621-.504 1.125-1.125 1.125h-9.75a1.125 1.125 0 0 1-1.125-1.125V7.875c0-.621.504-1.125 1.125-1.125H6.75a9.06 9.06 0 0 1 1.5.124m7.5 10.376h3.375c.621 0 1.125-.504 1.125-1.125V11.25c0-4.46-3.243-8.161-7.5-8.876a9.06 9.06 0 0 0-1.5-.124H9.375c-.621 0-1.125.504-1.125 1.125v3.5m7.5 10.375H9.375a1.125 1.125 0 0 1-1.125-1.125v-9.25m12 6.625v-1.875a3.375 3.375 0 0 0-3.375-3.375h-1.5a1.125 1.125 0 0 1-1.125-1.125v-1.5a3.375 3.375 0 0 0-3.375-3.375H9.75"
stroke-linecap="round" stroke-linejoin="round"></path>
</svg>

After

Width:  |  Height:  |  Size: 734 B

View File

@@ -1,4 +1,4 @@
@props(['node'])
@props(['node'])
<div class="relative w-full">
<x-menu>
@@ -40,6 +40,11 @@
{{ __('Rename') }}
</x-menu.item>
<x-menu.item wire:click="$parent.setTemplateFolder({{ $node->id }})" title="{{ __('Set as template folder') }}">
<x-icons.documentDuplicate class="w-4 h-4" />
{{ __('Template folder') }}
</x-menu.item>
<x-menu.item wire:confirm="{{ __('Are you sure you want to delete this folder?') }}"
wire:click="$parent.deleteNode({{ $node->id }})">
<x-icons.trash class="w-4 h-4" />