Files
lifeforge/apps/codeTime/server/utils
melvinchia3636 bc4ecf03d3 feat: backend migration complete
Former-commit-id: ceb74c1c4eb1720c3fb7fd30989e5b92044f224c [formerly 20731945f71827b17abbcdd95ceaf90770b9e663] [formerly 4c19721053a662a7dbc174621e8ec28e296e980b [formerly e62a1c5ba161854777e8b9b2bc07a550428b292a]]
Former-commit-id: 50144dc8556ce3062ea092e0a1c3005721ae551a [formerly 844a252350887b14c363a3ceb6faa89a0b29ca44]
Former-commit-id: 19c7019f1fb67c8e8a8940b5e07eba14c4474791
2025-10-04 15:29:43 +08:00
..
2025-10-04 15:29:43 +08:00
2025-10-04 15:29:43 +08:00
2025-10-04 15:29:43 +08:00

import { PBService } from '@functions/database'
import moment from 'moment'

import getStatistics from './statistics'

export default async function getReadmeHTML(pb: PBService) {
  const statistics = await getStatistics(pb)

  const today = moment().format('YYYY-MM-DD')

  const todayRecord = await pb.getList
    .collection('code_time__daily_entries')
    .page(1)
    .perPage(1)
    .filter([
      {
        field: 'date',
        operator: '=',
        value: `${today} 00:00:00.000Z`
      }
    ])
    .execute()

  const todayData = todayRecord.items[0]

  const todayTime = todayData ? todayData.total_minutes : 0

  return `
      <!DOCTYPE html>
  <html>
  
  <head>
    <script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
    <link rel="preconnect" href="https://fonts.googleapis.com" />
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
    <link href="https://fonts.googleapis.com/css2?family=Onest:wght@100..900&display=swap" rel="stylesheet" />
    <style>
      * {
        font-family: "Onest", sans-serif;
      }
        
      .border-lime {
        border-color: #cedd3e !important;
      }

      .bg-lime {
        background-color: #cedd3e;
      }

      .text-lime {
        color: #cedd3e;
      }
    </style>
  </head>
  
  <body class="flex p-4 text-zinc-100 flex-col w-full h-dvh border-lime border-2 bg-zinc-900">
    <header class="border-b-2 border-zinc-800 p-2 w-full flex items-center justify-between">
      <div class="flex items-center gap-2">
        <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" class="text-lime">
          <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
            d="m7 8l-4 4l4 4m10-8l4 4l-4 4M14 4l-4 16" />
        </svg>
        <h1 class="text-lg font-medium">Code Time Statistics</h1>
      </div>
      <div class="tracking-wider font-medium text-sm flex items-center gap-1">
        <span class="text-zinc-500">powered by</span>
        <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" class="text-lime">
          <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
            d="m11.414 10l-7.383 7.418a2.09 2.09 0 0 0 0 2.967a2.11 2.11 0 0 0 2.976 0L14.414 13m3.707 2.293l2.586-2.586a1 1 0 0 0 0-1.414l-7.586-7.586a1 1 0 0 0-1.414 0L9.121 6.293a1 1 0 0 0 0 1.414l7.586 7.586a1 1 0 0 0 1.414 0" />
        </svg>
        <span>Lifeforge<span class="text-lime">.</span></span>
      </div>
    </header>
    <div class="flex-1 w-full space-y-2 mt-4">
      <div class="bg-lime w-full p-2 flex items-center justify-between rounded-sm text-zinc-900">
        <div class="flex items-center gap-2 font-medium">
          <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
            <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
              d="M3 12a9 9 0 1 0 18 0a9 9 0 1 0-18 0m9 0l3 2m-3-7v5" />
          </svg>
          Total Time Spent
        </div>
        <div>
          <span class="text-2xl font-semibold tracking-wider">${Math.floor(
            moment.duration(statistics['Total time spent'], 'minutes').asHours()
          )}<span class="text-lime-800 text-xl">h</span> ${Math.floor(
            moment
              .duration(statistics['Total time spent'], 'minutes')
              .asMinutes() % 60
          )}<span
              class="text-lime-800 text-xl">m</span></span>
        </div>
      </div>
      <div
        class="bg-zinc-800/50 border-lime border-2 text-lime w-full p-2 flex items-center justify-between rounded-sm">
        <div class="flex items-center gap-2 font-medium">
          <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
            <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
              d="M3 12a9 9 0 0 0 5.998 8.485M21 12a9 9 0 1 0-18 0m9-5v5m0 3h2a1 1 0 0 1 1 1v1a1 1 0 0 1-1 1h-1a1 1 0 0 0-1 1v1a1 1 0 0 0 1 1h2m3-6v2a1 1 0 0 0 1 1h1m1-3v6" />
          </svg>
          Time Spent Today
        </div>
        <div>
          <span class="text-2xl font-semibold tracking-wider">${Math.floor(
            moment.duration(todayTime, 'minutes').asHours()
          )}<span class="text-zinc-600 text-xl">h</span> ${Math.floor(
            moment.duration(todayTime, 'minutes').asMinutes() % 60
          )}<span
              class="text-zinc-600 text-xl">m</span></span>
        </div>
      </div>
      <div class="bg-zinc-800/50 w-full p-2 flex items-center justify-between rounded-sm">
        <div class="flex items-center text-zinc-400 gap-2 font-medium">
          <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
            <g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2">
              <path
                d="M3 14c.83.642 2.077 1.017 3.5 1c1.423.017 2.67-.358 3.5-1s2.077-1.017 3.5-1c1.423-.017 2.67.358 3.5 1M8 3a2.4 2.4 0 0 0-1 2a2.4 2.4 0 0 0 1 2m4-4a2.4 2.4 0 0 0-1 2a2.4 2.4 0 0 0 1 2" />
              <path d="M3 10h14v5a6 6 0 0 1-6 6H9a6 6 0 0 1-6-6z" />
              <path d="M16.746 16.726a3 3 0 1 0 .252-5.555" />
            </g>
          </svg>
          Most Time Spent per Day
        </div>
        <div>
          <span class="text-2xl font-semibold tracking-wider">${Math.floor(
            moment.duration(statistics['Most time spent'], 'minutes').asHours()
          )}<span class="text-zinc-600 text-xl">h</span> ${Math.floor(
            moment
              .duration(statistics['Most time spent'], 'minutes')
              .asMinutes() % 60
          )}<span
              class="text-zinc-600 text-xl">m</span></span>
        </div>
      </div>
      <div class="bg-zinc-800/50 w-full p-2 flex items-center justify-between rounded-sm">
        <div class="flex items-center text-zinc-400 gap-2 font-medium">
          <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
            <g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2">
              <path d="M5 13a7 7 0 1 0 14 0a7 7 0 0 0-14 0m2-9L4.25 6M17 4l2.75 2" />
              <path d="M8 13h1l2 3l2-6l2 3h1" />
            </g>
          </svg>
          Average Time Spent per Day
        </div>
        <div>
          <span class="text-2xl font-semibold tracking-wider">${Math.floor(
            moment
              .duration(statistics['Average time spent'], 'minutes')
              .asHours()
          )}<span class="text-zinc-600 text-xl">h</span> ${Math.floor(
            moment
              .duration(statistics['Average time spent'], 'minutes')
              .asMinutes() % 60
          )}<span
              class="text-zinc-600 text-xl">m</span></span>
        </div>
      </div>
      <div class="flex items-center gap-2">
        <div class="bg-zinc-800/50 w-full p-2 flex items-center justify-between rounded-sm">
          <div class="flex items-center text-zinc-400 gap-2 font-medium">
            <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
              <g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2">
                <path d="M10 2c0-.88 1.056-1.331 1.692-.722c1.958 1.876 3.096 5.995 1.75 9.12l-.08.174l.012.003c.625.133 1.203-.43 2.303-2.173l.14-.224a1 1 0 0 1 1.582-.153C18.733 9.46 20 12.402 20 14.295C20 18.56 16.409 22 12 22s-8-3.44-8-7.706c0-2.252 1.022-4.716 2.632-6.301l.605-.589c.241-.236.434-.43.618-.624C9.285 5.268 10 3.856 10 2"  />
              </g>
            </svg>
            Current Streak
          </div>
          <div>
            <span class="text-2xl font-semibold tracking-wider">${
              statistics['Current streak']
            }<span
                class="text-zinc-600 text-xl ml-1">days</span></span>
          </div>
        </div>
        <div class="bg-zinc-800/50 w-full p-2 flex items-center justify-between rounded-sm">
          <div class="flex items-center text-zinc-400 gap-2 font-medium">
            <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" class="text-lime">
              <path fill="currentColor"
                d="M10 2c0-.88 1.056-1.331 1.692-.722c1.958 1.876 3.096 5.995 1.75 9.12l-.08.174l.012.003c.625.133 1.203-.43 2.303-2.173l.14-.224a1 1 0 0 1 1.582-.153C18.733 9.46 20 12.402 20 14.295C20 18.56 16.409 22 12 22s-8-3.44-8-7.706c0-2.252 1.022-4.716 2.632-6.301l.605-.589c.241-.236.434-.43.618-.624C9.285 5.268 10 3.856 10 2" />
            </svg>
            Longest Streak
          </div>
          <div>
            <span class="text-2xl font-semibold tracking-wider">${Math.max(
              statistics['Longest streak'],
              statistics['Current streak']
            )}<span
                class="text-zinc-600 text-xl ml-1">days</span></span>
          </div>
        </div>
      </div>
    </div>
    <div class="border-t-2 mt-4 border-zinc-800 flex items-center justify-between p-2 text-sm">
      <p class="flex text-zinc-500">[Computer Generated Report]</span>
      </p>
      <p class="flex text-zinc-500">Last updated: <span class="font-medium text-zinc-100 pl-1">${moment().format(
        'YYYY-MM-DD HH:mm:ss'
      )}</span>
      </p>
    </div>
  </body>
  
  </html>
    `
}