mirror of
https://github.com/brufdev/many-notes.git
synced 2026-01-28 13:59:10 -06:00
Add the note templates feature
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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');
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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');
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -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 |
@@ -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" />
|
||||
|
||||
Reference in New Issue
Block a user