Advertisement
Guest User

Sample HTML

a guest
Jun 15th, 2025
22
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 20.26 KB | None | 0 0
  1. var phase = @Phase
  2.  
  3. var raw = @JSON
  4.  
  5.  
  6.  
  7. var html = ""
  8.  
  9.  
  10.  
  11. // Parse the JSON string
  12.  
  13. var json = {};
  14.  
  15. try {
  16.  
  17. json = JSON.parse(raw);
  18.  
  19. } catch (e) {
  20.  
  21. // If parsing fails, create empty structure
  22.  
  23. json = {
  24.  
  25. de: [],
  26.  
  27. tsk: [],
  28.  
  29. item: [],
  30.  
  31. att: []
  32.  
  33. };
  34.  
  35. }
  36.  
  37.  
  38.  
  39. // Ensure all arrays exist
  40.  
  41. json.de = json.de || [];
  42.  
  43. json.tsk = json.tsk || [];
  44.  
  45. json.item = json.item || [];
  46.  
  47. json.att = json.att || [];
  48.  
  49.  
  50.  
  51. // Helper function to format currency
  52.  
  53. function formatCurrency(amount) {
  54.  
  55. return '$' + amount.toFixed(2);
  56.  
  57. }
  58.  
  59.  
  60.  
  61. // Helper function to format dates
  62.  
  63. function formatDate(dateStr) {
  64.  
  65. if (!dateStr || dateStr === "No Date") return "No Date";
  66.  
  67. const date = new Date(dateStr);
  68.  
  69. if (isNaN(date)) return dateStr;
  70.  
  71. return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' });
  72.  
  73. }
  74.  
  75.  
  76.  
  77. // Helper function to format date with time
  78.  
  79. function formatDateTime(dateStr) {
  80.  
  81. if (!dateStr) return null;
  82.  
  83. const date = new Date(dateStr);
  84.  
  85. if (isNaN(date)) return dateStr;
  86.  
  87. return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }) +
  88.  
  89. ' ' + date.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit', hour12: true });
  90.  
  91. }
  92.  
  93.  
  94.  
  95. // Helper function to get stage pill colors
  96.  
  97. function getStageColors(stage) {
  98.  
  99. switch (stage) {
  100.  
  101. case 'Design':
  102.  
  103. return { background: '#e5e7eb', color: '#374151' }; // Grey
  104.  
  105. case 'Quotation':
  106.  
  107. case 'Awaiting Client Approval':
  108.  
  109. return { background: '#fef3c7', color: '#92400e' }; // Yellow
  110.  
  111. case 'Approved by Client':
  112.  
  113. return { background: '#d1fae5', color: '#065f46' }; // Green
  114.  
  115. case 'Construction / Ordering':
  116.  
  117. return { background: '#ede9fe', color: '#6b21a8' }; // Purple
  118.  
  119. case 'Delivered / Installed':
  120.  
  121. return { background: '#dbeafe', color: '#1e40af' }; // Blue
  122.  
  123. case 'Abandoned':
  124.  
  125. return { background: '#fee2e2', color: '#dc2626' }; // Red
  126.  
  127. case 'Change Order - Awaiting Approval':
  128.  
  129. return { background: '#e5e7eb', color: '#374151' }; // Grey
  130.  
  131. default:
  132.  
  133. return { background: '#d1fae5', color: '#065f46' }; // Default green
  134.  
  135. }
  136.  
  137. }
  138.  
  139.  
  140.  
  141. // Helper function to get task status pill colors
  142.  
  143. function getTaskStatusColors(status) {
  144.  
  145. switch (status) {
  146.  
  147. case 'Not Started':
  148.  
  149. case 'Abandoned':
  150.  
  151. case 'Change Order - Awaiting Approval':
  152.  
  153. return { background: '#e5e7eb', color: '#374151' }; // Grey
  154.  
  155. case 'Open':
  156.  
  157. return { background: '#fef3c7', color: '#92400e' }; // Yellow
  158.  
  159. case 'Overdue':
  160.  
  161. return { background: '#fee2e2', color: '#dc2626' }; // Red
  162.  
  163. case 'On Hold':
  164.  
  165. return { background: '#dbeafe', color: '#1e40af' }; // Blue
  166.  
  167. case 'Completed':
  168.  
  169. return { background: '#d1fae5', color: '#065f46' }; // Green
  170.  
  171. default:
  172.  
  173. return { background: '#d1fae5', color: '#065f46' }; // Default green
  174.  
  175. }
  176.  
  177. }
  178.  
  179.  
  180.  
  181. // Helper function to get item status pill colors
  182.  
  183. function getItemStatusColors(status) {
  184.  
  185. switch (status) {
  186.  
  187. case 'Entered':
  188.  
  189. return { background: '#e5e7eb', color: '#374151' }; // Grey
  190.  
  191. case 'Approved - Needs Ordering':
  192.  
  193. case 'Received':
  194.  
  195. return { background: '#fef3c7', color: '#92400e' }; // Yellow
  196.  
  197. case 'Backordered':
  198.  
  199. return { background: '#ede9fe', color: '#6b21a8' }; // Purple
  200.  
  201. case 'On Hold':
  202.  
  203. return { background: '#dbeafe', color: '#1e40af' }; // Blue
  204.  
  205. case 'Installed':
  206.  
  207. return { background: '#d1fae5', color: '#065f46' }; // Green
  208.  
  209. case 'Abandoned':
  210.  
  211. return { background: '#fee2e2', color: '#dc2626' }; // Red
  212.  
  213. default:
  214.  
  215. return { background: '#d1fae5', color: '#065f46' }; // Default green
  216.  
  217. }
  218.  
  219. }
  220.  
  221.  
  222.  
  223. // Helper function to get attachment status pill colors
  224.  
  225. function getAttachmentStatusColors(status) {
  226.  
  227. switch (status) {
  228.  
  229. case 'Active':
  230.  
  231. return { background: '#d1fae5', color: '#065f46' }; // Green
  232.  
  233. case 'Inactive':
  234.  
  235. return { background: '#fee2e2', color: '#dc2626' }; // Red
  236.  
  237. default:
  238.  
  239. return { background: '#d1fae5', color: '#065f46' }; // Default green
  240.  
  241. }
  242.  
  243. }
  244.  
  245.  
  246.  
  247. // Helper function to get attachment stage pill colors
  248.  
  249. function getAttachmentStageColors(stage) {
  250.  
  251. switch (stage) {
  252.  
  253. case 'Initial Design':
  254.  
  255. return { background: '#fef3c7', color: '#92400e' }; // Yellow
  256.  
  257. case 'In Revisions':
  258.  
  259. case 'Abandoned':
  260.  
  261. return { background: '#fee2e2', color: '#dc2626' }; // Red
  262.  
  263. case 'Final':
  264.  
  265. return { background: '#d1fae5', color: '#065f46' }; // Green
  266.  
  267. default:
  268.  
  269. return { background: '#d1fae5', color: '#065f46' }; // Default green
  270.  
  271. }
  272.  
  273. }
  274.  
  275.  
  276.  
  277. // Helper function to get attachment type pill colors
  278.  
  279. function getAttachmentTypeColors(type) {
  280.  
  281. switch (type) {
  282.  
  283. case 'Floor Plan':
  284.  
  285. case 'Schedule':
  286.  
  287. case '3D Rendering':
  288.  
  289. return { background: '#d1fae5', color: '#065f46' }; // Green
  290.  
  291. case 'Inspiration':
  292.  
  293. return { background: '#fee2e2', color: '#dc2626' }; // Red
  294.  
  295. case 'Mood Board':
  296.  
  297. case 'Websites':
  298.  
  299. return { background: '#fef3c7', color: '#92400e' }; // Yellow
  300.  
  301. case 'Pre-Project Photos':
  302.  
  303. return { background: '#dbeafe', color: '#1e40af' }; // Blue
  304.  
  305. default:
  306.  
  307. return { background: '#d1fae5', color: '#065f46' }; // Default green
  308.  
  309. }
  310.  
  311. }
  312.  
  313.  
  314.  
  315. // Helper function to get attachment source pill colors
  316.  
  317. function getAttachmentSourceColors(source) {
  318.  
  319. switch (source) {
  320.  
  321. case 'Designer':
  322.  
  323. return { background: '#ede9fe', color: '#6b21a8' }; // Purple
  324.  
  325. case 'Client':
  326.  
  327. return { background: '#d1fae5', color: '#065f46' }; // Green
  328.  
  329. default:
  330.  
  331. return { background: '#d1fae5', color: '#065f46' }; // Default green
  332.  
  333. }
  334.  
  335. }
  336.  
  337.  
  338.  
  339. function createLink(id, name) {
  340.  
  341. return '<a href="https://tapeapp.com/jb-ventures/record/' + id + '" style="color: #0066cc; text-decoration: none;">' + name + '</a>';
  342.  
  343. }
  344.  
  345.  
  346.  
  347. // Helper function to render an item (used for all item displays)
  348.  
  349. function renderItem(item) {
  350.  
  351. var html = '<div style="margin-bottom: 8px; padding: 8px; background-color: #ffffff; border-radius: 4px; border: 1px solid #e1e8ed; position: relative;">';
  352.  
  353.  
  354.  
  355. // Status pill in top right with dynamic colors
  356.  
  357. var itemColors = getItemStatusColors(item.status);
  358.  
  359. html += '<span style="position: absolute; top: 8px; right: 8px; padding: 2px 6px; background-color: ' + itemColors.background + '; color: ' + itemColors.color + '; border-radius: 3px; font-size: 11px; font-weight: 500;">' + item.status + '</span>';
  360.  
  361.  
  362.  
  363. html += '<div style="font-weight: 500; color: #2c3e50; margin-bottom: 4px; font-size: 13px; padding-right: 60px;">' + createLink(item.id, item.name) + '</div>';
  364.  
  365.  
  366.  
  367. // Basic info
  368.  
  369. html += '<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); gap: 4px; margin-bottom: 6px; font-size: 11px; color: #666;">';
  370.  
  371. html += '<div><strong>Category:</strong> ' + item.cat + '</div>';
  372.  
  373. html += '<div><strong>Procurement:</strong> ' + item.res + '</div>';
  374.  
  375. html += '<div><strong>Unit Type:</strong> ' + item.unit_type + '</div>';
  376.  
  377. html += '<div><strong>Quantity:</strong> ' + item.qr + '</div>';
  378.  
  379. html += '</div>';
  380.  
  381.  
  382.  
  383. // Financials (reorganized into 4 columns)
  384.  
  385. html += '<div style="padding: 6px; background-color: #f8f9fa; border-radius: 3px; font-size: 10px;">';
  386.  
  387. html += '<div style="display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px; color: #555;">';
  388.  
  389.  
  390.  
  391. // Column 1: Suggested Retail, Multiplier, Estimated Cost
  392.  
  393. html += '<div>';
  394.  
  395. html += '<div style="margin-bottom: 2px;">Suggested Retail: ' + formatCurrency(item.price) + '</div>';
  396.  
  397. html += '<div style="margin-bottom: 2px;">Multiplier: ' + item.multi.toFixed(2) + '</div>';
  398.  
  399. html += '<div>Estimated Cost: ' + formatCurrency(item.price * item.multi * item.qr) + '</div>';
  400.  
  401. html += '</div>';
  402.  
  403.  
  404.  
  405. // Column 2: Retail MU, Retail MUA
  406.  
  407. html += '<div>';
  408.  
  409. html += '<div style="margin-bottom: 2px;">Retail MU: ' + item.rm.toFixed(2) + '</div>';
  410.  
  411. html += '<div>Retail MUA: ' + formatCurrency(item.price * item.multi * item.qr * item.rm) + '</div>';
  412.  
  413. html += '</div>';
  414.  
  415.  
  416.  
  417. // Column 3: Discount MU, Discount MUA
  418.  
  419. html += '<div>';
  420.  
  421. html += '<div style="margin-bottom: 2px;">Discount MU: ' + item.dm.toFixed(2) + '</div>';
  422.  
  423. html += '<div>Discount MUA: ' + formatCurrency(item.price * item.multi * item.qr * item.dm) + '</div>';
  424.  
  425. html += '</div>';
  426.  
  427.  
  428.  
  429. // Column 4: Difference, Potential Profit
  430.  
  431. html += '<div>';
  432.  
  433. html += '<div style="margin-bottom: 2px;">Difference: ' + formatCurrency((item.price * item.multi * item.qr * item.rm) - (item.price * item.multi * item.qr * item.dm)) + '</div>';
  434.  
  435. html += '<div>Potential Profit: ' + formatCurrency((item.price * item.multi * item.qr * item.dm) - (item.price * item.multi * item.qr)) + '</div>';
  436.  
  437. html += '</div>';
  438.  
  439.  
  440.  
  441. html += '</div>';
  442.  
  443. html += '</div>';
  444.  
  445.  
  446.  
  447. html += '</div>';
  448.  
  449. return html;
  450.  
  451. }
  452.  
  453.  
  454.  
  455. // Start building HTML
  456.  
  457. html += '<div style="font-family: -apple-system, BlinkMacSystemFont, \'Segoe UI\', Roboto, sans-serif; max-width: 768px; margin: 0 auto; padding: 10px; background-color: #f8f9fa;">';
  458.  
  459.  
  460.  
  461. // Project header (more compact)
  462.  
  463. html += '<div style="background-color: #ffffff; border-radius: 8px; padding: 16px; margin-bottom: 12px; box-shadow: 0 1px 4px rgba(0,0,0,0.08);">';
  464.  
  465. html += '<h1 style="margin: 0; color: #1a1a1a; font-size: 24px; font-weight: 600;">Project Summary</h1>';
  466.  
  467. html += '</div>';
  468.  
  469.  
  470.  
  471. // Check if all arrays are empty
  472.  
  473. var isEmpty = (json.de.length === 0 && json.tsk.length === 0 && json.item.length === 0 && json.att.length === 0);
  474.  
  475.  
  476.  
  477. if (isEmpty) {
  478.  
  479. // Show "No Related Items" card
  480.  
  481. html += '<div style="background-color: #ffffff; border-radius: 8px; padding: 16px; margin-bottom: 12px; box-shadow: 0 1px 4px rgba(0,0,0,0.08); text-align: center;">';
  482.  
  483. html += '<div style="color: #666; font-size: 16px; font-weight: 500;">No Related Items</div>';
  484.  
  485. html += '</div>';
  486.  
  487. } else {
  488.  
  489. // Group design elements by room
  490.  
  491. var roomGroups = {};
  492.  
  493. if (json.de && json.de.length > 0) {
  494.  
  495. json.de.forEach(function(de) {
  496.  
  497. if (!roomGroups[de.room_name]) {
  498.  
  499. roomGroups[de.room_name] = [];
  500.  
  501. }
  502.  
  503. roomGroups[de.room_name].push(de);
  504.  
  505. });
  506.  
  507. }
  508.  
  509.  
  510.  
  511. // Sort room names alphabetically
  512.  
  513. var sortedRooms = Object.keys(roomGroups).sort();
  514.  
  515.  
  516.  
  517. // Process each room
  518.  
  519. sortedRooms.forEach(function(roomName) {
  520.  
  521. html += '<div style="background-color: #ffffff; border-radius: 8px; padding: 16px; margin-bottom: 12px; box-shadow: 0 1px 4px rgba(0,0,0,0.08);">';
  522.  
  523. html += '<h2 style="margin: 0 0 12px 0; color: #2c3e50; font-size: 18px; font-weight: 600; border-bottom: 1px solid #e1e8ed; padding-bottom: 8px;">' + roomName + '</h2>';
  524.  
  525.  
  526.  
  527. roomGroups[roomName].forEach(function(de) {
  528.  
  529. html += '<div style="margin-bottom: 16px; padding: 12px; background-color: #f8f9fa; border-radius: 6px; border-left: 3px solid #3498db; position: relative;">';
  530.  
  531.  
  532.  
  533. // Stage pill in top right with dynamic colors
  534.  
  535. var stageColors = getStageColors(de.stage);
  536.  
  537. html += '<span style="position: absolute; top: 12px; right: 12px; padding: 2px 6px; background-color: ' + stageColors.background + '; color: ' + stageColors.color + '; border-radius: 3px; font-size: 11px; font-weight: 500;">' + de.stage + '</span>';
  538.  
  539.  
  540.  
  541. html += '<h3 style="margin: 0 0 8px 0; font-size: 16px; font-weight: 500; color: #2c3e50; padding-right: 60px;">' + createLink(de.id, de.name) + '</h3>';
  542.  
  543.  
  544.  
  545. // Tasks for this design element
  546.  
  547. var deTasks = json.tsk.filter(function(task) { return task.de_id === de.id; });
  548.  
  549. if (deTasks.length > 0) {
  550.  
  551. html += '<div style="margin-top: 12px;">';
  552.  
  553.  
  554.  
  555. deTasks.forEach(function(task) {
  556.  
  557. html += '<div style="margin-bottom: 8px; padding: 10px; background-color: #ffffff; border-radius: 4px; border: 1px solid #e1e8ed; position: relative;">';
  558.  
  559.  
  560.  
  561. // Status pill in top right with dynamic colors
  562.  
  563. var taskColors = getTaskStatusColors(task.status);
  564.  
  565. html += '<span style="position: absolute; top: 8px; right: 8px; padding: 3px 8px; background-color: ' + taskColors.background + '; color: ' + taskColors.color + '; border-radius: 3px; font-size: 11px; font-weight: 500;">' + task.status + '</span>';
  566.  
  567.  
  568.  
  569. html += '<h5 style="margin: 0 0 4px 0; font-size: 14px; font-weight: 500; color: #2c3e50; padding-right: 60px;">' + createLink(task.id, task.name) + '</h5>';
  570.  
  571.  
  572.  
  573. html += '<div style="font-size: 12px; color: #666; margin-bottom: 4px;">';
  574.  
  575. html += '<strong>Due:</strong> ' + formatDate(task.due);
  576.  
  577. html += '</div>';
  578.  
  579. html += '<div style="font-size: 12px; color: #666;">';
  580.  
  581. html += '<strong>Assigned To:</strong> ' + task.assigned.join(', ');
  582.  
  583. html += '</div>';
  584.  
  585.  
  586.  
  587. // Checklist items
  588.  
  589. if (task.checklist && task.checklist.length > 0) {
  590.  
  591. html += '<div style="margin-top: 6px; padding-top: 6px; border-top: 1px solid #e1e8ed;">';
  592.  
  593. task.checklist.forEach(function(item) {
  594.  
  595. html += '<div style="margin-bottom: 3px; padding-left: 12px; font-size: 11px; color: #555;">';
  596.  
  597. html += '<span style="color: ' + (item.completed ? '#4caf50' : '#999') + '; margin-right: 4px;">○</span>';
  598.  
  599. html += item.title;
  600.  
  601. if (item.description) {
  602.  
  603. html += ' <span style="color: #888; font-style: italic;">(' + item.description + ')</span>';
  604.  
  605. }
  606.  
  607. if (item.dueAt) {
  608.  
  609. html += ' <span style="color: #666; font-size: 10px;">[Due: ' + formatDateTime(item.dueAt) + ']</span>';
  610.  
  611. }
  612.  
  613. html += '</div>';
  614.  
  615. });
  616.  
  617. html += '</div>';
  618.  
  619. }
  620.  
  621.  
  622.  
  623. // Items for this task
  624.  
  625. var taskItems = json.item.filter(function(item) { return item.task_id === task.id; });
  626.  
  627. if (taskItems.length > 0) {
  628.  
  629. html += '<div style="margin-top: 8px; padding-top: 8px; border-top: 1px solid #e1e8ed;">';
  630.  
  631. taskItems.forEach(function(item) {
  632.  
  633. html += renderItem(item);
  634.  
  635. });
  636.  
  637. html += '</div>';
  638.  
  639. }
  640.  
  641.  
  642.  
  643. html += '</div>';
  644.  
  645. });
  646.  
  647.  
  648.  
  649. html += '</div>';
  650.  
  651. }
  652.  
  653.  
  654.  
  655. // Items for this design element (no task)
  656.  
  657. var deItems = json.item.filter(function(item) { return item.de_id === de.id && !item.task_id; });
  658.  
  659. if (deItems.length > 0) {
  660.  
  661. html += '<div style="margin-top: 12px;">';
  662.  
  663. html += '<h4 style="margin: 0 0 8px 0; font-size: 14px; font-weight: 500; color: #34495e;">Design Element Items</h4>';
  664.  
  665.  
  666.  
  667. deItems.forEach(function(item) {
  668.  
  669. html += renderItem(item);
  670.  
  671. });
  672.  
  673.  
  674.  
  675. html += '</div>';
  676.  
  677. }
  678.  
  679.  
  680.  
  681. html += '</div>';
  682.  
  683. });
  684.  
  685.  
  686.  
  687. html += '</div>';
  688.  
  689. });
  690.  
  691.  
  692.  
  693. // Project-specific tasks (orphaned tasks)
  694.  
  695. var projectTasks = json.tsk ? json.tsk.filter(function(task) { return !task.de_id; }) : [];
  696.  
  697. if (projectTasks.length > 0) {
  698.  
  699. html += '<div style="background-color: #ffffff; border-radius: 8px; padding: 16px; margin-bottom: 12px; box-shadow: 0 1px 4px rgba(0,0,0,0.08);">';
  700.  
  701. html += '<h2 style="margin: 0 0 12px 0; color: #2c3e50; font-size: 18px; font-weight: 600; border-bottom: 1px solid #e1e8ed; padding-bottom: 8px;">Project Specific Tasks</h2>';
  702.  
  703.  
  704.  
  705. projectTasks.forEach(function(task) {
  706.  
  707. html += '<div style="margin-bottom: 8px; padding: 10px; background-color: #f8f9fa; border-radius: 4px; border: 1px solid #e1e8ed; position: relative;">';
  708.  
  709.  
  710.  
  711. // Status pill in top right with dynamic colors
  712.  
  713. var taskColors = getTaskStatusColors(task.status);
  714.  
  715. html += '<span style="position: absolute; top: 8px; right: 8px; padding: 3px 8px; background-color: ' + taskColors.background + '; color: ' + taskColors.color + '; border-radius: 3px; font-size: 11px; font-weight: 500;">' + task.status + '</span>';
  716.  
  717.  
  718.  
  719. html += '<h5 style="margin: 0 0 4px 0; font-size: 14px; font-weight: 500; color: #2c3e50; padding-right: 60px;">' + createLink(task.id, task.name) + '</h5>';
  720.  
  721.  
  722.  
  723. html += '<div style="font-size: 12px; color: #666; margin-bottom: 4px;">';
  724.  
  725. html += '<strong>Due:</strong> ' + formatDate(task.due);
  726.  
  727. html += '</div>';
  728.  
  729. html += '<div style="font-size: 12px; color: #666;">';
  730.  
  731. html += '<strong>Assigned To:</strong> ' + task.assigned.join(', ');
  732.  
  733. html += '</div>';
  734.  
  735.  
  736.  
  737. // Checklist items
  738.  
  739. if (task.checklist && task.checklist.length > 0) {
  740.  
  741. html += '<div style="margin-top: 6px; padding-top: 6px; border-top: 1px solid #e1e8ed;">';
  742.  
  743. task.checklist.forEach(function(item) {
  744.  
  745. html += '<div style="margin-bottom: 3px; padding-left: 12px; font-size: 11px; color: #555;">';
  746.  
  747. html += '<span style="color: ' + (item.completed ? '#4caf50' : '#999') + '; margin-right: 4px;">○</span>';
  748.  
  749. html += item.title;
  750.  
  751. if (item.description) {
  752.  
  753. html += ' <span style="color: #888; font-style: italic;">(' + item.description + ')</span>';
  754.  
  755. }
  756.  
  757. if (item.dueAt) {
  758.  
  759. html += ' <span style="color: #666; font-size: 10px;">[Due: ' + formatDateTime(item.dueAt) + ']</span>';
  760.  
  761. }
  762.  
  763. html += '</div>';
  764.  
  765. });
  766.  
  767. html += '</div>';
  768.  
  769. }
  770.  
  771.  
  772.  
  773. html += '</div>';
  774.  
  775. });
  776.  
  777.  
  778.  
  779. html += '</div>';
  780.  
  781. }
  782.  
  783.  
  784.  
  785. // Project-specific items
  786.  
  787. var projectItems = json.item ? json.item.filter(function(item) { return !item.de_id && !item.task_id; }) : [];
  788.  
  789. if (projectItems.length > 0) {
  790.  
  791. html += '<div style="background-color: #ffffff; border-radius: 8px; padding: 16px; margin-bottom: 12px; box-shadow: 0 1px 4px rgba(0,0,0,0.08);">';
  792.  
  793. html += '<h2 style="margin: 0 0 12px 0; color: #2c3e50; font-size: 18px; font-weight: 600; border-bottom: 1px solid #e1e8ed; padding-bottom: 8px;">Project Specific Items</h2>';
  794.  
  795.  
  796.  
  797. projectItems.forEach(function(item) {
  798.  
  799. html += renderItem(item);
  800.  
  801. });
  802.  
  803.  
  804.  
  805. html += '</div>';
  806.  
  807. }
  808.  
  809.  
  810.  
  811. // Attachments
  812.  
  813. if (json.att && json.att.length > 0) {
  814.  
  815. html += '<div style="background-color: #ffffff; border-radius: 8px; padding: 16px; margin-bottom: 12px; box-shadow: 0 1px 4px rgba(0,0,0,0.08);">';
  816.  
  817. html += '<h2 style="margin: 0 0 12px 0; color: #2c3e50; font-size: 18px; font-weight: 600; border-bottom: 1px solid #e1e8ed; padding-bottom: 8px;">Attachments</h2>';
  818.  
  819.  
  820.  
  821. json.att.forEach(function(att) {
  822.  
  823. html += '<div style="margin-bottom: 8px; padding: 10px 10px ' + (att.type && att.type.length > 0 ? '35px' : '10px') + ' 10px; background-color: #f8f9fa; border-radius: 4px; border: 1px solid #e1e8ed; position: relative;">';
  824.  
  825.  
  826.  
  827. // Source, Status, and Stage pills in top right (first row)
  828.  
  829. html += '<div style="position: absolute; top: 8px; right: 8px; display: flex; gap: 4px;">';
  830.  
  831.  
  832.  
  833. // Source pill (first)
  834.  
  835. var sourceColors = getAttachmentSourceColors(att.source);
  836.  
  837. html += '<span style="padding: 2px 6px; background-color: ' + sourceColors.background + '; color: ' + sourceColors.color + '; border-radius: 3px; font-size: 11px; font-weight: 500;">' + att.source + '</span>';
  838.  
  839.  
  840.  
  841. // Status pill (second)
  842.  
  843. var statusColors = getAttachmentStatusColors(att.status);
  844.  
  845. html += '<span style="padding: 2px 6px; background-color: ' + statusColors.background + '; color: ' + statusColors.color + '; border-radius: 3px; font-size: 11px; font-weight: 500;">' + att.status + '</span>';
  846.  
  847.  
  848.  
  849. // Stage pill (third)
  850.  
  851. var stageColors = getAttachmentStageColors(att.stage);
  852.  
  853. html += '<span style="padding: 2px 6px; background-color: ' + stageColors.background + '; color: ' + stageColors.color + '; border-radius: 3px; font-size: 11px; font-weight: 500;">' + att.stage + '</span>';
  854.  
  855.  
  856.  
  857. html += '</div>';
  858.  
  859.  
  860.  
  861. // Type pills on second row (flush right)
  862.  
  863. if (att.type && att.type.length > 0) {
  864.  
  865. html += '<div style="position: absolute; bottom: 8px; right: 8px; display: flex; gap: 4px;">';
  866.  
  867. att.type.sort().forEach(function(type) {
  868.  
  869. var typeColors = getAttachmentTypeColors(type);
  870.  
  871. html += '<span style="padding: 2px 6px; background-color: ' + typeColors.background + '; color: ' + typeColors.color + '; border-radius: 3px; font-size: 11px; font-weight: 500;">' + type + '</span>';
  872.  
  873. });
  874.  
  875. html += '</div>';
  876.  
  877. }
  878.  
  879.  
  880.  
  881. html += '<h5 style="margin: 0 0 6px 0; font-size: 13px; font-weight: 500; color: #2c3e50; padding-right: 50%;">' + createLink(att.id, att.name) + '</h5>';
  882.  
  883. html += '</div>';
  884.  
  885. });
  886.  
  887.  
  888.  
  889. html += '</div>';
  890.  
  891. }
  892.  
  893. }
  894.  
  895.  
  896.  
  897. html += '</div>';
  898.  
  899.  
  900.  
  901. html
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement