Commit iniziale di eminuxCRM: CRM Rails con pipeline, campagne email e Docker.
Include autenticazione, progetti isolati, mail marketing HTML con SMTP, test A/B e editor WYSIWYG. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
<% if object.errors.any? %>
|
||||
<div class="mb-4 rounded-lg border border-rose-200 bg-rose-50 px-4 py-3 text-sm text-rose-800 dark:border-rose-900 dark:bg-rose-950 dark:text-rose-100">
|
||||
<ul class="list-disc pl-4">
|
||||
<% object.errors.full_messages.each do |msg| %>
|
||||
<li><%= msg %></li>
|
||||
<% end %>
|
||||
</ul>
|
||||
</div>
|
||||
<% end %>
|
||||
@@ -0,0 +1,6 @@
|
||||
<% if notice.present? %>
|
||||
<div class="mb-4 rounded-lg border border-zinc-200 bg-white px-4 py-3 text-sm text-zinc-800 dark:border-zinc-700 dark:bg-zinc-900 dark:text-zinc-100"><%= notice %></div>
|
||||
<% end %>
|
||||
<% if alert.present? %>
|
||||
<div class="mb-4 rounded-lg border border-rose-200 bg-rose-50 px-4 py-3 text-sm text-rose-800 dark:border-rose-900 dark:bg-rose-950 dark:text-rose-100"><%= alert %></div>
|
||||
<% end %>
|
||||
@@ -0,0 +1,27 @@
|
||||
<% size = local_assigns.fetch(:size, :md) %>
|
||||
<% variant = local_assigns.fetch(:variant, :full) %>
|
||||
<% mark_class = { xs: "size-6", sm: "size-7", md: "size-8", lg: "size-10", xl: "size-12" }.fetch(size) %>
|
||||
<% show_wordmark = variant != :mark %>
|
||||
|
||||
<% mark = capture do %>
|
||||
<span class="inline-flex <%= mark_class %> shrink-0 items-center justify-center rounded-lg bg-zinc-900 text-white dark:bg-zinc-100 dark:text-zinc-950" aria-hidden="true">
|
||||
<svg viewBox="0 0 24 24" class="size-[62%]" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round">
|
||||
<path d="M17.2 8.15a6.85 6.85 0 1 0 0 7.7" />
|
||||
<path d="M6.4 12h11.1" />
|
||||
</svg>
|
||||
</span>
|
||||
<% end %>
|
||||
|
||||
<% if show_wordmark %>
|
||||
<span class="inline-flex items-center gap-2.5">
|
||||
<%= mark %>
|
||||
<span class="leading-tight">
|
||||
<span class="block text-[15px] font-semibold tracking-tight">eminux<span class="font-medium text-zinc-400">CRM</span></span>
|
||||
<% if local_assigns[:subtitle].present? %>
|
||||
<span class="block text-xs font-normal text-zinc-500"><%= subtitle %></span>
|
||||
<% end %>
|
||||
</span>
|
||||
</span>
|
||||
<% else %>
|
||||
<%= mark %>
|
||||
<% end %>
|
||||
@@ -0,0 +1,17 @@
|
||||
<div class="<%= card_class %> p-4" data-controller="merge-tokens">
|
||||
<h3 class="text-sm font-semibold">Variabili</h3>
|
||||
<p class="mt-1 text-xs text-zinc-500">Clicca per inserirle nell’oggetto o nel testo, nel punto del cursore. All’invio diventano i dati della scheda.</p>
|
||||
<ul class="mt-3 space-y-1 text-xs">
|
||||
<% MailMerge.catalog.each do |token, label| %>
|
||||
<li>
|
||||
<button type="button"
|
||||
class="flex w-full items-baseline justify-between gap-2 rounded-md px-1 py-1 text-left hover:bg-zinc-100 dark:hover:bg-zinc-800"
|
||||
data-action="merge-tokens#insert"
|
||||
data-merge-tokens-token-param="<%= token %>">
|
||||
<code class="rounded bg-zinc-100 px-1.5 py-0.5 text-zinc-800 dark:bg-zinc-800 dark:text-zinc-100">{{<%= token %>}}</code>
|
||||
<span class="text-zinc-500"><%= label %></span>
|
||||
</button>
|
||||
</li>
|
||||
<% end %>
|
||||
</ul>
|
||||
</div>
|
||||
@@ -0,0 +1,27 @@
|
||||
<div class="overflow-hidden rounded-xl border border-zinc-200 bg-white text-zinc-900 dark:border-zinc-800 dark:bg-zinc-900 dark:text-zinc-100">
|
||||
<div class="divide-y divide-zinc-100 dark:divide-zinc-800">
|
||||
<% tasks.each do |task| %>
|
||||
<div class="flex flex-wrap items-center gap-3 px-4 py-3 <%= 'bg-rose-50' if task.overdue? %> <%= 'bg-amber-50' if task.due_today? %>">
|
||||
<div class="min-w-0 flex-1">
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<%= link_to task.organization.name, task.organization, class: "font-medium text-zinc-900 hover:underline dark:text-zinc-100" %>
|
||||
<%= priority_badge(task.priority) %>
|
||||
<span class="text-xs text-zinc-500"><%= task.task_type_label %></span>
|
||||
</div>
|
||||
<div class="mt-0.5 text-sm text-zinc-700 dark:text-zinc-200"><%= task.title %></div>
|
||||
<div class="mt-0.5 text-xs text-zinc-500">
|
||||
<%= task.contact&.full_name || "—" %> · <%= format_dt(task.due_at) %>
|
||||
<% if task.assigned_user %>· <%= task.assigned_user.full_name %><% end %>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<%= button_to "Completa", complete_task_path(task), method: :patch, class: "rounded-md bg-emerald-600 px-3 py-1.5 text-xs font-medium text-white hover:bg-emerald-700" %>
|
||||
<%= link_to "Apri", task.organization, class: "rounded-md border border-zinc-300 px-3 py-1.5 text-xs font-medium text-zinc-700 hover:bg-zinc-50 dark:border-zinc-600 dark:text-zinc-200 dark:hover:bg-zinc-800" %>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
<% if tasks.blank? %>
|
||||
<div class="px-4 py-6 text-center text-sm text-slate-500">Nessun task</div>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,14 @@
|
||||
<button type="button"
|
||||
data-controller="theme"
|
||||
data-action="theme#toggle"
|
||||
class="inline-flex size-9 items-center justify-center rounded-lg border border-zinc-200 text-zinc-600 hover:bg-zinc-100 dark:border-zinc-700 dark:text-zinc-300 dark:hover:bg-zinc-800"
|
||||
aria-label="Passa al tema scuro o chiaro"
|
||||
title="Tema">
|
||||
<svg class="size-4 dark:hidden" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" aria-hidden="true">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z" />
|
||||
</svg>
|
||||
<svg class="hidden size-4 dark:block" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" aria-hidden="true">
|
||||
<circle cx="12" cy="12" r="4" />
|
||||
<path stroke-linecap="round" d="M12 3v1.5M12 19.5V21M4.5 12H3M21 12h-1.5M6.2 6.2l-1-1M18.8 18.8l-1-1M6.2 17.8l-1 1M18.8 5.2l-1 1" />
|
||||
</svg>
|
||||
</button>
|
||||
@@ -0,0 +1,14 @@
|
||||
<div class="relative" data-controller="dropdown">
|
||||
<button type="button" data-action="dropdown#toggle" class="flex items-center gap-2 rounded-lg px-2 py-1.5 text-sm text-zinc-600 hover:bg-zinc-100 dark:text-zinc-300 dark:hover:bg-zinc-800">
|
||||
<span class="max-w-[12rem] truncate font-medium text-zinc-900 dark:text-zinc-100"><%= current_user.full_name %></span>
|
||||
<span class="text-xs text-zinc-400"><%= current_user.admin? ? "Admin" : "Utente" %></span>
|
||||
</button>
|
||||
<div data-dropdown-target="menu" class="absolute right-0 z-30 mt-1 hidden w-48 rounded-lg border border-zinc-200 bg-white py-1 shadow-lg dark:border-zinc-700 dark:bg-zinc-900">
|
||||
<% if current_user.admin? %>
|
||||
<%= link_to "Impostazioni", admin_path, class: "block px-4 py-2 text-sm text-zinc-700 hover:bg-zinc-50 dark:text-zinc-200 dark:hover:bg-zinc-800" %>
|
||||
<%= link_to "Utenti", users_path, class: "block px-4 py-2 text-sm text-zinc-700 hover:bg-zinc-50 dark:text-zinc-200 dark:hover:bg-zinc-800" %>
|
||||
<% end %>
|
||||
<%= link_to "Password", edit_password_path, class: "block px-4 py-2 text-sm text-zinc-700 hover:bg-zinc-50 dark:text-zinc-200 dark:hover:bg-zinc-800" %>
|
||||
<%= button_to "Esci", logout_path, method: :delete, class: "block w-full px-4 py-2 text-left text-sm text-zinc-700 hover:bg-zinc-50 dark:text-zinc-200 dark:hover:bg-zinc-800" %>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,10 @@
|
||||
<div data-controller="wysiwyg" data-wysiwyg-upload-url-value="<%= mail_images_path %>" class="space-y-1">
|
||||
<label class="mb-1 block text-sm font-medium" for="<%= "#{form.object_name}_#{method}" %>"><%= label %></label>
|
||||
<%= form.hidden_field method, data: { wysiwyg_target: "input" } %>
|
||||
<trix-editor
|
||||
input="<%= "#{form.object_name}_#{method}" %>"
|
||||
class="trix-content block min-h-56 w-full"
|
||||
data-wysiwyg-target="editor"
|
||||
data-action="trix-file-accept->wysiwyg#acceptFile trix-attachment-add->wysiwyg#upload"></trix-editor>
|
||||
<p class="text-xs text-zinc-500">Formato, elenchi, link e immagini. Clicca un’immagine e trascina gli angoli per ridimensionarla. Le variabili a destra si inseriscono nel punto del cursore.</p>
|
||||
</div>
|
||||
Reference in New Issue
Block a user