PluginProbe
Easy Invoice – Invoice Generator, PDF Quotes & Payments / 2.4.0
Easy Invoice – Invoice Generator, PDF Quotes & Payments v2.4.0
2.4.3 2.4.4 2.4.2 2.4.0 2.4.1 2.3.8 2.3.7 2.3.6 2.3.5 2.3.4 2.3.3 2.3.2 2.3.1 2.2.0 2.1.21 2.1.20 2.1.19 2.1.18 2.1.0 2.1.1 2.1.10 2.1.11 2.1.12 2.1.13 2.1.14 All 60 releases
easy-invoice / templates / quote-templates / minimalist.php

minimalist.php in Easy Invoice – Invoice Generator, PDF Quotes & Payments 2.4.0, at templates/quote-templates/minimalist.php

564 lines 20.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Minimalist Quote Template - Clean & Simple Design
4 * Minimalist, clean, and simple quote template
5 */
6
7 if ( ! defined( 'ABSPATH' ) ) {
8 exit;
9 }
10 $text_settings = \EasyInvoice\Helpers\TemplateTextHelper::getQuoteTextSettings();
11 $company_info = \EasyInvoice\Helpers\TemplateTextHelper::getCompanyInfo();
12 // Ensure a formatter is available
13 if (!isset($formatter) || !$formatter) {
14 $formatter = new \EasyInvoice\Helpers\QuoteFormatter($quote ?? null);
15 }
16 ?>
17 <style>
18 /* Minimalist Quote Template - Clean & Simple Design */
19 :root {
20 --primary-color: #374151;
21 --primary-light: #6b7280;
22 --primary-dark: #1f2937;
23 --secondary-color: #9ca3af;
24 --accent-color: #3b82f6;
25 --success-color: #10b981;
26 --warning-color: #f59e0b;
27 --error-color: #ef4444;
28 --text-primary: #111827;
29 --text-secondary: #6b7280;
30 --text-muted: #9ca3af;
31 --border-color: #d1d5db;
32 --border-light: #e5e7eb;
33 --background-primary: #ffffff;
34 --background-secondary: #f9fafb;
35 --background-accent: #f3f4f6;
36
37 --font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
38 --font-family-heading: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
39
40 --font-size-xs: 0.75rem;
41 --font-size-sm: 0.875rem;
42 --font-size-base: 1rem;
43 --font-size-lg: 1.125rem;
44 --font-size-xl: 1.25rem;
45 --font-size-2xl: 1.5rem;
46 --font-size-3xl: 1.875rem;
47 --font-size-4xl: 2.25rem;
48
49 --spacing-1: 0.25rem;
50 --spacing-2: 0.5rem;
51 --spacing-3: 0.75rem;
52 --spacing-4: 1rem;
53 --spacing-5: 1.25rem;
54 --spacing-6: 1.5rem;
55 --spacing-8: 2rem;
56 --spacing-10: 2.5rem;
57 --spacing-12: 3rem;
58 --spacing-16: 4rem;
59 --spacing-20: 5rem;
60
61 --radius-sm: 0.25rem;
62 --radius-md: 0.375rem;
63 --radius-lg: 0.5rem;
64 --radius-xl: 0.75rem;
65 --radius-2xl: 1rem;
66
67 --shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05);
68 --shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
69 --shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
70 --shadow-xl: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1);
71 }
72
73 .template-minimalist {
74 font-family: var(--font-family);
75 background: var(--background-primary);
76 color: var(--text-primary);
77 max-width: 800px;
78 margin: 0 auto;
79 padding: var(--spacing-8);
80 line-height: 1.6;
81 }
82
83 /* Header Section */
84 .template-minimalist .quote-header {
85 display: flex;
86 justify-content: space-between;
87 align-items: flex-start;
88 margin-bottom: var(--spacing-12);
89 padding-bottom: var(--spacing-8);
90 border-bottom: 1px solid var(--border-light);
91 }
92
93 .template-minimalist .company-info {
94 flex: 1;
95 }
96
97 .template-minimalist .company-logo {
98 margin-bottom: 0;
99 }
100
101 .template-minimalist .logo-image {
102 max-height: 60px;
103 max-width: 200px;
104 object-fit: contain;
105 filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.1));
106 }
107
108 .template-minimalist .company-name {
109 font-family: var(--font-family-heading);
110 font-size: var(--font-size-2xl);
111 font-weight: 700;
112 color: var(--text-primary);
113 margin-bottom: var(--spacing-3);
114 line-height: 1.2;
115 }
116
117 .template-minimalist .company-details {
118 color: var(--text-secondary);
119 font-size: var(--font-size-sm);
120 line-height: 1.5;
121 }
122
123 .template-minimalist .quote-meta {
124 text-align: right;
125 min-width: 200px;
126 }
127
128 .template-minimalist .quote-title {
129 font-family: var(--font-family-heading);
130 font-size: var(--font-size-xl);
131 font-weight: 600;
132 color: var(--text-primary);
133 margin-bottom: var(--spacing-4);
134 }
135
136 .template-minimalist .meta-item {
137 display: flex;
138 justify-content: space-between;
139 align-items: center;
140 margin-bottom: var(--spacing-2);
141 padding-bottom: var(--spacing-2);
142 border-bottom: 1px solid var(--border-light);
143 }
144
145 .template-minimalist .meta-item:last-child {
146 margin-bottom: 0;
147 padding-bottom: 0;
148 border-bottom: none;
149 }
150
151 .template-minimalist .meta-label {
152 font-size: var(--font-size-sm);
153 color: var(--text-secondary);
154 font-weight: 400;
155 }
156
157 .template-minimalist .meta-value {
158 font-size: var(--font-size-sm);
159 color: var(--text-primary);
160 font-weight: 500;
161 }
162
163 /* Address Section */
164 .template-minimalist .address-section {
165 display: grid;
166 grid-template-columns: 1fr 1fr;
167 gap: var(--spacing-8);
168 margin-bottom: var(--spacing-12);
169 }
170
171 .template-minimalist .address-block h2 {
172 font-family: var(--font-family-heading);
173 font-size: var(--font-size-base);
174 font-weight: 600;
175 color: var(--text-primary);
176 margin-bottom: var(--spacing-3);
177 }
178
179 .template-minimalist .client-name {
180 font-weight: 600;
181 color: var(--text-primary);
182 font-size: var(--font-size-base);
183 margin-bottom: var(--spacing-2);
184 }
185
186 .template-minimalist .address-content {
187 color: var(--text-secondary);
188 font-size: var(--font-size-sm);
189 line-height: 1.5;
190 }
191
192 /* Quote Items Table */
193 .template-minimalist .quote-items {
194 width: 100%;
195 border-collapse: collapse;
196 margin-bottom: var(--spacing-12);
197 background: var(--background-primary);
198 border: 1px solid var(--border-color);
199 }
200
201 .template-minimalist .quote-items th {
202 background: var(--background-secondary);
203 color: var(--text-primary);
204 font-weight: 600;
205 text-align: left;
206 padding: var(--spacing-3) var(--spacing-4);
207 font-size: var(--font-size-sm);
208 border-bottom: 1px solid var(--border-color);
209 }
210
211 .template-minimalist .quote-items td {
212 padding: var(--spacing-3) var(--spacing-4);
213 border-bottom: 1px solid var(--border-light);
214 vertical-align: top;
215 }
216
217 .template-minimalist .quote-items tr:last-child td {
218 border-bottom: none;
219 }
220
221 .template-minimalist .item-name .item-title {
222 font-weight: 500;
223 color: var(--text-primary);
224 margin-bottom: var(--spacing-1);
225 }
226
227 .template-minimalist .item-description {
228 color: var(--text-muted);
229 font-size: var(--font-size-sm);
230 line-height: 1.4;
231 font-weight: normal;
232 }
233
234 .template-minimalist .item-quantity,
235 .template-minimalist .item-price,
236 .template-minimalist .item-total {
237 text-align: right;
238 font-weight: 400;
239 color: var(--text-primary);
240 }
241
242 /* Quote Totals */
243 .template-minimalist .quote-totals {
244 margin-left: auto;
245 width: 300px;
246 background: var(--background-secondary);
247 padding: var(--spacing-6);
248 border: 1px solid var(--border-color);
249 }
250
251 .template-minimalist .quote-total-row {
252 display: flex;
253 justify-content: space-between;
254 align-items: center;
255 padding: var(--spacing-2) 0;
256 border-bottom: 1px solid var(--border-light);
257 }
258
259 .template-minimalist .quote-total-row:last-child {
260 border-bottom: none;
261 }
262
263 .template-minimalist .quote-total-label {
264 font-weight: 400;
265 color: var(--text-secondary);
266 }
267
268 .template-minimalist .quote-total-value {
269 font-weight: 600;
270 color: var(--text-primary);
271 }
272
273 .template-minimalist .quote-totals .quote-grand-total {
274 font-size: var(--font-size-lg);
275 font-weight: 700;
276 color: var(--text-primary);
277 border-top: 2px solid var(--border-color);
278 padding-top: var(--spacing-3);
279 margin-top: var(--spacing-3);
280 }
281
282 /* Notes Section */
283 .template-minimalist .quote-notes {
284 margin-top: var(--spacing-8);
285 padding: var(--spacing-6);
286 background: var(--background-secondary);
287 border-radius: var(--radius-lg);
288 border: 1px solid var(--border-color);
289 }
290
291 .template-minimalist .quote-notes h3 {
292 font-family: var(--font-family-heading);
293 font-size: var(--font-size-lg);
294 font-weight: 600;
295 color: var(--text-primary);
296 margin-bottom: var(--spacing-3);
297 border-bottom: 1px solid var(--border-color);
298 padding-bottom: var(--spacing-2);
299 }
300
301 .template-minimalist .notes-content {
302 color: var(--text-secondary);
303 font-size: var(--font-size-sm);
304 line-height: 1.6;
305 }
306
307 .template-minimalist .quote-terms {
308 margin-top: var(--spacing-8);
309 padding: var(--spacing-6);
310 background: var(--background-secondary);
311 border-radius: var(--radius-lg);
312 border: 1px solid var(--border-color);
313 }
314
315 .template-minimalist .quote-terms h3 {
316 font-family: var(--font-family-heading);
317 font-size: var(--font-size-lg);
318 font-weight: 600;
319 color: var(--text-primary);
320 margin-bottom: var(--spacing-3);
321 border-bottom: 1px solid var(--border-color);
322 padding-bottom: var(--spacing-2);
323 }
324
325 .template-minimalist .terms-content {
326 color: var(--text-secondary);
327 font-size: var(--font-size-sm);
328 line-height: 1.6;
329 }
330
331 .template-minimalist .quote-footer {
332 margin-top: var(--spacing-8);
333 padding-top: var(--spacing-6);
334 border-top: 2px solid var(--border-light);
335 text-align: center;
336 color: var(--text-secondary);
337 font-size: var(--font-size-sm);
338 }
339
340 /* Footer */
341 .template-minimalist .quote-footer p {
342 margin: 0;
343 color: var(--text-secondary);
344 font-size: var(--font-size-sm);
345 }
346
347 /* Responsive Design */
348 @media (max-width: 768px) {
349 .template-minimalist {
350 padding: var(--spacing-4);
351 }
352
353 .template-minimalist .quote-header {
354 flex-direction: column;
355 gap: var(--spacing-6);
356 }
357
358 .template-minimalist .quote-meta {
359 text-align: left;
360 min-width: auto;
361 }
362
363 .template-minimalist .address-section {
364 grid-template-columns: 1fr;
365 gap: var(--spacing-6);
366 }
367
368 .template-minimalist .quote-totals {
369 width: 100%;
370 }
371 }
372
373 /* Shared polish: numbers line up, numeric headings sit over their figures. */
374 .template-minimalist .quote-items th + th { text-align: right; }
375 .template-minimalist .quote-items .item-quantity,
376 .template-minimalist .quote-items .item-price,
377 .template-minimalist .quote-items .item-adjust,
378 .template-minimalist .quote-items .item-total,
379 .template-minimalist .quote-total-value { font-variant-numeric: tabular-nums; white-space: nowrap; }
380 .template-minimalist .meta-value { white-space: nowrap; }
381 .template-minimalist .quote-items .item-adjust { text-align: right; }
382 .template-minimalist .client-contact { font-weight: 500; margin-bottom: 0.15em; }
383 .template-minimalist .client-tax-number,
384 .template-minimalist .company-tax-number { margin-top: 0.35em; }
385 .template-minimalist .quote-items .item-description { white-space: pre-line; }
386 .template-minimalist .document-kind { display: block; font-size: 0.72rem; font-weight: 600; letter-spacing: 0.16em; text-transform: uppercase; color: var(--text-muted, #8a8a8a); font-family: var(--font-family-body, inherit); font-style: normal; margin-bottom: 0.3em; line-height: 1.2; }
387
388 /* Minimalist: hairlines only — no filled panels anywhere. */
389 .template-minimalist .quote-header { margin-bottom: var(--spacing-8); padding-bottom: var(--spacing-6); border-bottom: 1px solid var(--border-color); }
390 .template-minimalist .address-section { margin-bottom: var(--spacing-8); }
391 .template-minimalist .address-block h2 { font-size: var(--font-size-xs); text-transform: uppercase; letter-spacing: 0.14em; color: var(--text-muted); font-weight: 600; margin-bottom: var(--spacing-2); }
392 .template-minimalist .quote-items { border: 0; margin-bottom: var(--spacing-6); }
393 .template-minimalist .quote-items th { background: transparent; color: var(--text-muted); font-size: var(--font-size-xs); text-transform: uppercase; letter-spacing: 0.12em; padding: var(--spacing-2) var(--spacing-3); border-bottom: 1px solid var(--text-primary); }
394 .template-minimalist .quote-items td { padding: var(--spacing-4) var(--spacing-3); }
395 .template-minimalist .quote-items th:first-child, .template-minimalist .quote-items td:first-child { padding-left: 0; }
396 .template-minimalist .quote-items th:last-child, .template-minimalist .quote-items td:last-child { padding-right: 0; }
397 .template-minimalist .quote-totals { background: transparent; border: 0; padding: 0; width: 320px; }
398 .template-minimalist .quote-total-row { padding: var(--spacing-2) 0; border-bottom: 1px solid var(--border-light); }
399 .template-minimalist .quote-totals .quote-grand-total { border-top: 1px solid var(--text-primary); border-bottom: 0; margin-top: var(--spacing-1); padding-top: var(--spacing-3); font-size: var(--font-size-lg); }
400 .template-minimalist .quote-totals .quote-grand-total .quote-total-value { font-size: var(--font-size-xl); }
401 .template-minimalist .quote-notes, .template-minimalist .quote-terms { background: transparent; border: 0; border-top: 1px solid var(--border-light); border-radius: 0; padding: var(--spacing-5) 0 0; margin-top: var(--spacing-6); }
402 .template-minimalist .quote-notes h3, .template-minimalist .quote-terms h3 { font-size: var(--font-size-xs); text-transform: uppercase; letter-spacing: 0.14em; color: var(--text-muted); font-weight: 600; margin-bottom: var(--spacing-2); border: 0; padding: 0; }
403 .template-minimalist .quote-footer { background: transparent; border: 0; border-top: 1px solid var(--border-light); padding: var(--spacing-4) 0 0; margin-top: var(--spacing-8); color: var(--text-muted); font-size: var(--font-size-sm); }
404 .template-minimalist { padding: var(--spacing-12) var(--spacing-10); }
405 .template-minimalist .quote-header { display: block; text-align: center; border-bottom: 0; padding-bottom: 0; }
406 .template-minimalist .company-info { margin-bottom: var(--spacing-6); }
407 .template-minimalist .company-logo { display: flex; justify-content: center; }
408 .template-minimalist .company-details { max-width: 420px; margin: 0 auto; }
409 .template-minimalist .quote-meta { text-align: center; min-width: 0; max-width: 360px; margin: 0 auto; border-top: 1px solid var(--text-primary); border-bottom: 1px solid var(--text-primary); padding: var(--spacing-4) 0; }
410 .template-minimalist .quote-title { font-size: var(--font-size-2xl); font-weight: 500; letter-spacing: 0.02em; margin-bottom: var(--spacing-3); }
411 .template-minimalist .meta-item { justify-content: space-between; border-bottom: 0; padding: var(--spacing-1) 0; margin: 0; }
412 .template-minimalist .meta-label { text-transform: uppercase; letter-spacing: 0.12em; font-size: var(--font-size-xs); color: var(--text-muted); }
413 .template-minimalist .address-section { margin-top: var(--spacing-8); }
414 @media (max-width: 640px) { .template-minimalist { padding: var(--spacing-6) var(--spacing-4); } }
415 </style>
416
417 <div class="template template-minimalist">
418 <div class="quote-header">
419 <div class="company-info">
420 <?php if (!empty($company_info['logo'])): ?>
421 <div class="company-logo">
422 <img src="<?php echo esc_url($company_info['logo']); ?>" alt="<?php echo esc_attr($company_info['name']); ?>" class="logo-image" onerror="this.parentNode.parentNode.removeChild(this.parentNode)">
423 </div>
424 <?php endif; ?>
425 <div class="company-name"><?php echo esc_html($company_info['name']); ?></div>
426 <div class="company-details">
427 <?php echo wp_kses_post(\EasyInvoice\Helpers\TemplateTextHelper::generateFromSection($company_info, false, 'details-only')); ?>
428 </div>
429 </div>
430
431 <div class="quote-meta">
432 <?php $ei_kind = \EasyInvoice\Controllers\SettingsController::getTextQuote(); $ei_title = trim((string) $quote->getTitle()); ?>
433 <div class="quote-title"><?php if ('' !== $ei_title && 0 !== strcasecmp($ei_title, $ei_kind)) : ?><span class="document-kind"><?php echo esc_html($ei_kind); ?></span><?php endif; ?><?php echo esc_html('' !== $ei_title ? $ei_title : $ei_kind); ?></div>
434 <div class="meta-item">
435 <span class="meta-label"><?php echo esc_html($text_settings['quote_number']); ?></span>
436 <span class="meta-value"><?php echo esc_html($quote->getNumber()); ?></span>
437 </div>
438 <div class="meta-item">
439 <span class="meta-label"><?php echo esc_html($text_settings['quote_date']); ?></span>
440 <span class="meta-value"><?php echo esc_html(\EasyInvoice\Controllers\SettingsController::formatDate($quote->getIssueDate())); ?></span>
441 </div>
442 <div class="meta-item">
443 <span class="meta-label"><?php echo esc_html($text_settings['valid_until']); ?></span>
444 <span class="meta-value"><?php echo esc_html(\EasyInvoice\Controllers\SettingsController::formatDate($quote->getExpiryDate())); ?></span>
445 </div>
446 </div>
447 </div>
448
449 <?php $ei_client_html = \EasyInvoice\Helpers\TemplateTextHelper::generateClientBlock($quote); if ('' !== $ei_client_html) : ?>
450 <div class="address-section">
451 <div class="address-block">
452 <h2><?php echo esc_html($text_settings['to']); ?></h2>
453 <div class="address-content">
454 <?php echo wp_kses_post($ei_client_html); ?>
455 </div>
456 </div>
457 </div>
458 <?php endif; ?>
459
460 <?php if ($quote->getDescription()): ?>
461 <div class="quote-description" style="margin-bottom: 1.5rem;">
462 <div class="description-content" style="color:#6b7280; line-height:1.6; font-size:0.875rem; white-space:pre-wrap;">
463 <?php echo wp_kses_post($quote->getDescription()); ?>
464 </div>
465 </div>
466 <?php endif; ?>
467
468 <?php
469 // The adjustment column appears only when at least one line uses it.
470 $ei_show_adjust = \EasyInvoice\Controllers\SettingsController::shouldShowQuoteAdjustField();
471 if ($ei_show_adjust) {
472 $ei_show_adjust = false;
473 foreach ($quote->getItems() as $ei_item) {
474 if ((float) $ei_item->getAdjustPercentage() != 0.0) { $ei_show_adjust = true; break; }
475 }
476 }
477 ?>
478 <table class="quote-items">
479 <thead>
480 <tr>
481 <th><?php echo esc_html($text_settings['service']); ?></th>
482 <th><?php echo esc_html($text_settings['qty']); ?></th>
483 <th><?php echo esc_html($text_settings['rate_price']); ?></th>
484 <?php if ($ei_show_adjust): ?>
485 <th><?php echo esc_html($text_settings['adjust']); ?> (%)</th>
486 <?php endif; ?>
487 <th><?php echo esc_html($text_settings['line_total'] ?? $text_settings['total']); ?></th>
488 </tr>
489 </thead>
490 <tbody>
491 <?php if (!$quote->getItems()) : ?>
492 <tr class="no-items"><td colspan="<?php echo $ei_show_adjust ? 5 : 4; ?>" style="text-align:center;color:#9ca3af;padding:18px;"><?php esc_html_e('No items yet.', 'easy-invoice'); ?></td></tr>
493 <?php endif; ?>
494 <?php foreach ($quote->getItems() as $item): ?>
495 <tr>
496 <td class="item-name">
497 <div class="item-title"><?php echo esc_html($item->getName()); ?></div>
498 <?php if ($item->getDescription()): ?>
499 <div class="item-description"><?php echo esc_html($item->getDescription()); ?></div>
500 <?php endif; ?>
501 </td>
502 <td class="item-quantity"><?php echo esc_html($item->getQuantity() ?: '0'); ?></td>
503 <td class="item-price"><?php echo esc_html($formatter->format($item->getPrice())); ?></td>
504 <?php if ($ei_show_adjust): ?>
505 <td class="item-adjust"><?php
506 $adjust_percentage = $item->getAdjustPercentage();
507 if ($adjust_percentage != 0) {
508 echo esc_html($adjust_percentage > 0 ? '+' : '') . esc_html($adjust_percentage) . '%';
509 } else {
510 echo esc_html__('—', 'easy-invoice');
511 }
512 ?></td>
513 <?php endif; ?>
514 <td class="item-total"><?php echo esc_html($formatter->format($item->getAmount())); ?></td>
515 </tr>
516 <?php endforeach; ?>
517 </tbody>
518 </table>
519
520 <div class="quote-totals">
521 <?php echo wp_kses_post(\EasyInvoice\Helpers\TemplateTextHelper::generateTotalsSection($quote, $text_settings, $formatter, 'quote')); ?>
522 </div>
523
524 <?php if ($quote->getNotes()): ?>
525 <div class="quote-notes">
526 <h3><?php esc_html_e('Notes', 'easy-invoice'); ?></h3>
527 <div class="notes-content">
528 <?php echo wp_kses_post($quote->getNotes() ?: ''); ?>
529 </div>
530 </div>
531 <?php endif; ?>
532
533 <?php
534 $terms_conditions = $quote->getTerms() ?: \EasyInvoice\Controllers\SettingsController::getQuoteTermsConditions();
535 if ($terms_conditions):
536 ?>
537 <div class="quote-terms">
538 <h3><?php esc_html_e('Terms & Conditions', 'easy-invoice'); ?></h3>
539 <div class="terms-content">
540 <?php echo wp_kses_post($terms_conditions); ?>
541 </div>
542 </div>
543 <?php endif; ?>
544
545 <?php
546 /**
547 * Fires inside the document card, after notes and terms and before the
548 * footer text: attachments, the signature block, e-invoicing notes. Being
549 * inside the card, it is part of what "Download as PDF" captures.
550 *
551 * @param object $document The quote model.
552 * @param string $type 'quote'.
553 */
554 do_action('easy_invoice_document_body_after', $quote, 'quote');
555
556 $ei_footer_text = \EasyInvoice\Helpers\TemplateTextHelper::footerText($quote, 'quote');
557 if ('' !== trim(wp_strip_all_tags((string) $ei_footer_text))) :
558 ?>
559 <div class="quote-footer">
560 <p><?php echo wp_kses_post($ei_footer_text); ?></p>
561 </div>
562 <?php endif; ?>
563 </div>
564