PluginProbe
Easy Invoice – Invoice Generator, PDF Quotes & Payments / 2.4.0
Easy Invoice – Invoice Generator, PDF Quotes & Payments v2.4.0
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 2.1.15 2.1.16 2.1.2 All 57 releases
easy-invoice / templates / quote-templates / minimal.php

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

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