update: submit struk bg
parent
5e8b735ea9
commit
b95e93d3c2
|
|
@ -1,10 +1,26 @@
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using System.Text;
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
namespace eSPJ.Controllers.SpjDriverUpstController
|
namespace eSPJ.Controllers.SpjDriverUpstController
|
||||||
{
|
{
|
||||||
[Route("upst/submit")]
|
[Route("upst/submit")]
|
||||||
public class SubmitController : Controller
|
public class SubmitController : Controller
|
||||||
{
|
{
|
||||||
|
private readonly IHttpClientFactory _httpClientFactory;
|
||||||
|
private readonly IConfiguration _configuration;
|
||||||
|
private readonly IWebHostEnvironment _env;
|
||||||
|
|
||||||
|
public SubmitController(
|
||||||
|
IHttpClientFactory httpClientFactory,
|
||||||
|
IConfiguration configuration,
|
||||||
|
IWebHostEnvironment env)
|
||||||
|
{
|
||||||
|
_httpClientFactory = httpClientFactory;
|
||||||
|
_configuration = configuration;
|
||||||
|
_env = env;
|
||||||
|
}
|
||||||
|
|
||||||
[HttpGet("")]
|
[HttpGet("")]
|
||||||
public IActionResult Index()
|
public IActionResult Index()
|
||||||
|
|
@ -12,70 +28,235 @@ namespace eSPJ.Controllers.SpjDriverUpstController
|
||||||
return View("~/Views/Admin/Transport/SpjDriverUpst/Submit/Index.cshtml");
|
return View("~/Views/Admin/Transport/SpjDriverUpst/Submit/Index.cshtml");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
[HttpGet("struk")]
|
[HttpGet("struk")]
|
||||||
public IActionResult Struk()
|
public IActionResult Struk()
|
||||||
{
|
{
|
||||||
return View("~/Views/Admin/Transport/SpjDriverUpst/Submit/Struk.cshtml");
|
return View("~/Views/Admin/Transport/SpjDriverUpst/Submit/Struk.cshtml");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpPost("ocr-struk")]
|
||||||
|
[IgnoreAntiforgeryToken]
|
||||||
|
public async Task<IActionResult> OcrStruk(IFormFile? Foto)
|
||||||
|
{
|
||||||
|
if (Foto == null || Foto.Length == 0)
|
||||||
|
return BadRequest(new { success = false, message = "Foto tidak ditemukan." });
|
||||||
|
|
||||||
|
if (Foto.Length > 10 * 1024 * 1024)
|
||||||
|
return BadRequest(new { success = false, message = "Ukuran foto terlalu besar. Maksimal 10MB." });
|
||||||
|
|
||||||
|
var apiKey = _configuration["OpenRouter:OCRkey"];
|
||||||
|
if (string.IsNullOrWhiteSpace(apiKey))
|
||||||
|
return StatusCode(500, new { success = false, message = "OpenRouter API key belum diset." });
|
||||||
|
|
||||||
|
byte[] fileBytes;
|
||||||
|
await using (var ms = new MemoryStream())
|
||||||
|
{
|
||||||
|
await Foto.CopyToAsync(ms);
|
||||||
|
fileBytes = ms.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
var mimeType = string.IsNullOrWhiteSpace(Foto.ContentType) ? "image/jpeg" : Foto.ContentType;
|
||||||
|
var base64 = Convert.ToBase64String(fileBytes);
|
||||||
|
var dataUrl = $"data:{mimeType};base64,{base64}";
|
||||||
|
|
||||||
|
var payload = new
|
||||||
|
{
|
||||||
|
model = "google/gemini-2.5-flash-image",
|
||||||
|
temperature = 0,
|
||||||
|
messages = new object[]
|
||||||
|
{
|
||||||
|
new
|
||||||
|
{
|
||||||
|
role = "user",
|
||||||
|
content = new object[]
|
||||||
|
{
|
||||||
|
new
|
||||||
|
{
|
||||||
|
type = "text",
|
||||||
|
text = @"Baca data dari foto struk timbang kendaraan ini. Ekstrak semua data yang tersedia ke dalam format JSON berikut:
|
||||||
|
{
|
||||||
|
""nomorStruk"": ""<nomor struk tanpa prefix, contoh: 8001441>"",
|
||||||
|
""nomorPolisi"": ""<nomor polisi kendaraan, contoh: B 9125 PJA>"",
|
||||||
|
""penugasan"": ""<area penugasan, contoh: JAKARTA BARAT>"",
|
||||||
|
""waktuMasuk"": ""<waktu masuk format YYYY-MM-DD, HH:MM:SS>"",
|
||||||
|
""waktuKeluar"": ""<waktu keluar format YYYY-MM-DD, HH:MM:SS>"",
|
||||||
|
""beratMasuk"": <berat masuk dalam kg sebagai angka integer tanpa desimal>,
|
||||||
|
""beratKeluar"": <berat keluar dalam kg sebagai angka integer tanpa desimal>,
|
||||||
|
""beratNett"": <berat netto dalam kg sebagai angka integer tanpa desimal>
|
||||||
|
}
|
||||||
|
|
||||||
|
Catatan penting:
|
||||||
|
- nomorStruk: ambil angka utama saja, abaikan prefix bulan seperti '03_' atau '03 '
|
||||||
|
- waktu: konversi ke format YYYY-MM-DD, HH:MM:SS (contoh: 2025-08-04, 08:13:51)
|
||||||
|
- berat: hanya angka integer tanpa satuan 'kg'
|
||||||
|
- Jika data tidak ada atau tidak terbaca, isi dengan null
|
||||||
|
- Kembalikan HANYA JSON valid tanpa penjelasan atau markdown code block"
|
||||||
|
},
|
||||||
|
new
|
||||||
|
{
|
||||||
|
type = "image_url",
|
||||||
|
image_url = new { url = dataUrl }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var json = JsonSerializer.Serialize(payload);
|
||||||
|
var request = new HttpRequestMessage(HttpMethod.Post, "https://openrouter.ai/api/v1/chat/completions");
|
||||||
|
request.Headers.TryAddWithoutValidation("Authorization", $"Bearer {apiKey}");
|
||||||
|
request.Headers.TryAddWithoutValidation("Accept", "application/json");
|
||||||
|
request.Headers.TryAddWithoutValidation("HTTP-Referer", "https://pesapakawan.dinaslhdki.id");
|
||||||
|
request.Headers.TryAddWithoutValidation("X-Title", "eSPJ OCR Struk");
|
||||||
|
request.Content = new StringContent(json, Encoding.UTF8, "application/json");
|
||||||
|
|
||||||
|
var client = _httpClientFactory.CreateClient();
|
||||||
|
using var response = await client.SendAsync(request);
|
||||||
|
var responseText = await response.Content.ReadAsStringAsync();
|
||||||
|
|
||||||
|
if (!response.IsSuccessStatusCode)
|
||||||
|
{
|
||||||
|
return StatusCode((int)response.StatusCode, new
|
||||||
|
{
|
||||||
|
success = false,
|
||||||
|
message = "OpenRouter request gagal.",
|
||||||
|
detail = responseText
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
using var doc = JsonDocument.Parse(responseText);
|
||||||
|
var content = doc.RootElement
|
||||||
|
.GetProperty("choices")[0]
|
||||||
|
.GetProperty("message")
|
||||||
|
.GetProperty("content")
|
||||||
|
.GetString() ?? "";
|
||||||
|
|
||||||
|
var jsonMatch = Regex.Match(content, @"\{[\s\S]*\}");
|
||||||
|
if (!jsonMatch.Success)
|
||||||
|
return Ok(new { success = false, message = "AI tidak mengembalikan data valid.", raw = content });
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using var dataDoc = JsonDocument.Parse(jsonMatch.Value);
|
||||||
|
var root = dataDoc.RootElement;
|
||||||
|
return Ok(new
|
||||||
|
{
|
||||||
|
success = true,
|
||||||
|
data = new
|
||||||
|
{
|
||||||
|
nomorStruk = GetStringValue(root, "nomorStruk"),
|
||||||
|
nomorPolisi = GetStringValue(root, "nomorPolisi"),
|
||||||
|
penugasan = GetStringValue(root, "penugasan"),
|
||||||
|
waktuMasuk = GetStringValue(root, "waktuMasuk"),
|
||||||
|
waktuKeluar = GetStringValue(root, "waktuKeluar"),
|
||||||
|
beratMasuk = GetIntValue(root, "beratMasuk"),
|
||||||
|
beratKeluar = GetIntValue(root, "beratKeluar"),
|
||||||
|
beratNett = GetIntValue(root, "beratNett")
|
||||||
|
},
|
||||||
|
raw = content
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return Ok(new { success = false, message = "Gagal memproses respons AI.", raw = content });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[HttpPost("struk")]
|
[HttpPost("struk")]
|
||||||
public IActionResult ProcessStruk(string NomorStruk, string NomorPolisi, string Penugasan,
|
public async Task<IActionResult> ProcessStruk(
|
||||||
string WaktuMasuk, string WaktuKeluar, int? BeratMasuk, int? BeratKeluar, int BeratNett)
|
string? NomorStruk,
|
||||||
|
string? NomorPolisi,
|
||||||
|
string? Penugasan,
|
||||||
|
string? WaktuMasuk,
|
||||||
|
string? WaktuKeluar,
|
||||||
|
int? BeratMasuk,
|
||||||
|
int? BeratKeluar,
|
||||||
|
int? BeratNett,
|
||||||
|
string? Timbang,
|
||||||
|
IFormFile? FotoStruk)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Validate required inputs
|
if (string.IsNullOrWhiteSpace(NomorStruk))
|
||||||
if (string.IsNullOrEmpty(NomorStruk) || BeratNett <= 0)
|
|
||||||
{
|
{
|
||||||
TempData["Error"] = "Nomor struk dan berat nett harus diisi.";
|
TempData["Error"] = "Nomor struk wajib diisi.";
|
||||||
return RedirectToAction("Struk");
|
return RedirectToAction("Struk");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate receipt number format (numbers only, 7+ digits)
|
if (!Regex.IsMatch(NomorStruk, @"^\d{3,}$"))
|
||||||
if (!System.Text.RegularExpressions.Regex.IsMatch(NomorStruk, @"^\d{7,}$"))
|
|
||||||
{
|
{
|
||||||
TempData["Error"] = "Format nomor struk tidak valid. Harus berupa angka minimal 7 digit.";
|
TempData["Error"] = "Format nomor struk tidak valid. Harus berupa angka minimal 3 digit.";
|
||||||
return RedirectToAction("Struk");
|
return RedirectToAction("Struk");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate weight range
|
if (string.IsNullOrWhiteSpace(NomorPolisi))
|
||||||
if (BeratNett < 100 || BeratNett > 50000)
|
|
||||||
{
|
{
|
||||||
TempData["Error"] = "Berat nett harus antara 100 kg - 50,000 kg.";
|
TempData["Error"] = "Nomor polisi wajib diisi.";
|
||||||
return RedirectToAction("Struk");
|
return RedirectToAction("Struk");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate optional weights
|
if (string.IsNullOrWhiteSpace(Penugasan))
|
||||||
if (BeratMasuk.HasValue && (BeratMasuk < 0 || BeratMasuk > 100000))
|
|
||||||
{
|
{
|
||||||
TempData["Error"] = "Berat masuk tidak valid.";
|
TempData["Error"] = "Penugasan wajib diisi.";
|
||||||
return RedirectToAction("Struk");
|
return RedirectToAction("Struk");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (BeratKeluar.HasValue && (BeratKeluar < 0 || BeratKeluar > 100000))
|
if (string.IsNullOrWhiteSpace(WaktuMasuk))
|
||||||
{
|
{
|
||||||
TempData["Error"] = "Berat keluar tidak valid.";
|
TempData["Error"] = "Waktu masuk wajib diisi.";
|
||||||
return RedirectToAction("Struk");
|
return RedirectToAction("Struk");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Here you would normally save to database
|
if (string.IsNullOrWhiteSpace(WaktuKeluar))
|
||||||
// For now, just simulate success with all data
|
|
||||||
var submitData = new
|
|
||||||
{
|
{
|
||||||
NomorStruk,
|
TempData["Error"] = "Waktu keluar wajib diisi.";
|
||||||
NomorPolisi = NomorPolisi ?? "N/A",
|
return RedirectToAction("Struk");
|
||||||
Penugasan = Penugasan ?? "N/A",
|
}
|
||||||
WaktuMasuk = WaktuMasuk ?? "N/A",
|
|
||||||
WaktuKeluar = WaktuKeluar ?? "N/A",
|
|
||||||
BeratMasuk = BeratMasuk?.ToString() ?? "N/A",
|
|
||||||
BeratKeluar = BeratKeluar?.ToString() ?? "N/A",
|
|
||||||
BeratNett
|
|
||||||
};
|
|
||||||
|
|
||||||
TempData["Success"] = $"Struk berhasil disubmit! No: {NomorStruk}, Nett: {BeratNett} kg";
|
if (!BeratMasuk.HasValue || BeratMasuk <= 0)
|
||||||
|
{
|
||||||
|
TempData["Error"] = "Berat masuk wajib diisi.";
|
||||||
|
return RedirectToAction("Struk");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!BeratKeluar.HasValue || BeratKeluar <= 0)
|
||||||
|
{
|
||||||
|
TempData["Error"] = "Berat keluar wajib diisi.";
|
||||||
|
return RedirectToAction("Struk");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!BeratNett.HasValue || BeratNett <= 0)
|
||||||
|
{
|
||||||
|
TempData["Error"] = "Berat nett wajib diisi.";
|
||||||
|
return RedirectToAction("Struk");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (FotoStruk == null || FotoStruk.Length == 0)
|
||||||
|
{
|
||||||
|
TempData["Error"] = "Foto struk wajib dilampirkan.";
|
||||||
|
return RedirectToAction("Struk");
|
||||||
|
}
|
||||||
|
|
||||||
|
string? fotoStrukUrl = null;
|
||||||
|
if (FotoStruk != null && FotoStruk.Length > 0)
|
||||||
|
{
|
||||||
|
var datePart = DateTime.Now.ToString("yyyy-MM-dd");
|
||||||
|
var uploadPath = Path.Combine(_env.ContentRootPath, "uploads", "struk", datePart);
|
||||||
|
if (!Directory.Exists(uploadPath))
|
||||||
|
Directory.CreateDirectory(uploadPath);
|
||||||
|
|
||||||
|
var ext = Path.GetExtension(FotoStruk.FileName).ToLowerInvariant();
|
||||||
|
if (string.IsNullOrEmpty(ext)) ext = ".jpg";
|
||||||
|
var fileName = $"struk_{NomorStruk}_{Guid.NewGuid():N}{ext}";
|
||||||
|
var filePath = Path.Combine(uploadPath, fileName);
|
||||||
|
await using var stream = new FileStream(filePath, FileMode.Create);
|
||||||
|
await FotoStruk.CopyToAsync(stream);
|
||||||
|
fotoStrukUrl = $"/uploads/struk/{datePart}/{fileName}";
|
||||||
|
}
|
||||||
|
|
||||||
|
var timbangLabel = Timbang == "RDF" ? "Timbangan RDF" : "Timbangan TPA";
|
||||||
|
TempData["Success"] = $"Struk berhasil disubmit! No: {NomorStruk}, {timbangLabel}, Nett: {BeratNett!.Value} kg";
|
||||||
return RedirectToAction("Index", "Home");
|
return RedirectToAction("Index", "Home");
|
||||||
|
|
||||||
}
|
}
|
||||||
catch (Exception)
|
catch (Exception)
|
||||||
{
|
{
|
||||||
|
|
@ -83,5 +264,24 @@ namespace eSPJ.Controllers.SpjDriverUpstController
|
||||||
return RedirectToAction("Struk");
|
return RedirectToAction("Struk");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static string? GetStringValue(JsonElement root, string key)
|
||||||
|
{
|
||||||
|
if (root.TryGetProperty(key, out var prop) && prop.ValueKind == JsonValueKind.String)
|
||||||
|
return prop.GetString();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int? GetIntValue(JsonElement root, string key)
|
||||||
|
{
|
||||||
|
if (root.TryGetProperty(key, out var prop))
|
||||||
|
{
|
||||||
|
if (prop.ValueKind == JsonValueKind.Number && prop.TryGetInt32(out var val))
|
||||||
|
return val;
|
||||||
|
if (prop.ValueKind == JsonValueKind.String && int.TryParse(prop.GetString(), out var strVal))
|
||||||
|
return strVal;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="min-w-0 flex-1">
|
<div class="min-w-0 flex-1">
|
||||||
<p class="text-[10px] font-black uppercase tracking-[0.22em] text-gray-400">Install App</p>
|
<p class="text-[10px] font-black uppercase tracking-[0.22em] text-gray-400">Install App</p>
|
||||||
<h3 class="mt-1 text-sm font-black tracking-tight">Pasang eSPJ di perangkat</h3>
|
<h3 class="mt-1 text-sm font-black tracking-tight">PKM UPST di perangkat</h3>
|
||||||
<p id="pwaInstallDescription" class="mt-1 text-[11px] leading-relaxed text-gray-500/80">Akses lebih cepat langsung dari home screen tanpa buka browser dulu.</p>
|
<p id="pwaInstallDescription" class="mt-1 text-[11px] leading-relaxed text-gray-500/80">Akses lebih cepat langsung dari home screen tanpa buka browser dulu.</p>
|
||||||
</div>
|
</div>
|
||||||
<button id="pwaInstallDismiss" type="button" class="flex h-9 w-9 items-center justify-center rounded-xl bg-white/10 text-lg font-bold text-gray-500/80 transition active:scale-90">×</button>
|
<button id="pwaInstallDismiss" type="button" class="flex h-9 w-9 items-center justify-center rounded-xl bg-white/10 text-lg font-bold text-gray-500/80 transition active:scale-90">×</button>
|
||||||
|
|
@ -26,7 +26,7 @@
|
||||||
<div id="pwaInstallHelpModal" class="fixed inset-0 z-130 hidden items-end justify-center bg-slate-950/60 p-4 backdrop-blur-sm sm:items-center">
|
<div id="pwaInstallHelpModal" class="fixed inset-0 z-130 hidden items-end justify-center bg-slate-950/60 p-4 backdrop-blur-sm sm:items-center">
|
||||||
<div class="w-full max-w-sm rounded-4xl bg-white p-6 shadow-2xl">
|
<div class="w-full max-w-sm rounded-4xl bg-white p-6 shadow-2xl">
|
||||||
<p class="text-[10px] font-black uppercase tracking-[0.24em] text-upst">Cara Install</p>
|
<p class="text-[10px] font-black uppercase tracking-[0.24em] text-upst">Cara Install</p>
|
||||||
<h3 class="mt-2 text-lg font-black tracking-tight text-slate-900">Pasang eSPJ ke home screen</h3>
|
<h3 class="mt-2 text-lg font-black tracking-tight text-slate-900">Pasang PKM UPST ke home screen</h3>
|
||||||
<p id="pwaInstallHelpText" class="mt-3 text-sm leading-relaxed text-slate-600">Gunakan menu browser lalu pilih install aplikasi.</p>
|
<p id="pwaInstallHelpText" class="mt-3 text-sm leading-relaxed text-slate-600">Gunakan menu browser lalu pilih install aplikasi.</p>
|
||||||
|
|
||||||
<div id="pwaInstallHelpSteps" class="mt-5 grid gap-2 rounded-2xl bg-slate-50 p-4 text-xs leading-relaxed text-slate-600">
|
<div id="pwaInstallHelpSteps" class="mt-5 grid gap-2 rounded-2xl bg-slate-50 p-4 text-xs leading-relaxed text-slate-600">
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -434,6 +434,9 @@
|
||||||
.right-full {
|
.right-full {
|
||||||
right: 100%;
|
right: 100%;
|
||||||
}
|
}
|
||||||
|
.-bottom-0 {
|
||||||
|
bottom: calc(var(--spacing) * -0);
|
||||||
|
}
|
||||||
.-bottom-0\.5 {
|
.-bottom-0\.5 {
|
||||||
bottom: calc(var(--spacing) * -0.5);
|
bottom: calc(var(--spacing) * -0.5);
|
||||||
}
|
}
|
||||||
|
|
@ -479,6 +482,9 @@
|
||||||
.left-0 {
|
.left-0 {
|
||||||
left: calc(var(--spacing) * 0);
|
left: calc(var(--spacing) * 0);
|
||||||
}
|
}
|
||||||
|
.left-1 {
|
||||||
|
left: calc(var(--spacing) * 1);
|
||||||
|
}
|
||||||
.left-1\/2 {
|
.left-1\/2 {
|
||||||
left: calc(1/2 * 100%);
|
left: calc(1/2 * 100%);
|
||||||
}
|
}
|
||||||
|
|
@ -794,6 +800,9 @@
|
||||||
.-mr-16 {
|
.-mr-16 {
|
||||||
margin-right: calc(var(--spacing) * -16);
|
margin-right: calc(var(--spacing) * -16);
|
||||||
}
|
}
|
||||||
|
.mr-1 {
|
||||||
|
margin-right: calc(var(--spacing) * 1);
|
||||||
|
}
|
||||||
.mr-1\.5 {
|
.mr-1\.5 {
|
||||||
margin-right: calc(var(--spacing) * 1.5);
|
margin-right: calc(var(--spacing) * 1.5);
|
||||||
}
|
}
|
||||||
|
|
@ -884,6 +893,9 @@
|
||||||
.aspect-square {
|
.aspect-square {
|
||||||
aspect-ratio: 1 / 1;
|
aspect-ratio: 1 / 1;
|
||||||
}
|
}
|
||||||
|
.h-0 {
|
||||||
|
height: calc(var(--spacing) * 0);
|
||||||
|
}
|
||||||
.h-0\.5 {
|
.h-0\.5 {
|
||||||
height: calc(var(--spacing) * 0.5);
|
height: calc(var(--spacing) * 0.5);
|
||||||
}
|
}
|
||||||
|
|
@ -989,6 +1001,9 @@
|
||||||
.h-full {
|
.h-full {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
.max-h-48 {
|
||||||
|
max-height: calc(var(--spacing) * 48);
|
||||||
|
}
|
||||||
.min-h-screen {
|
.min-h-screen {
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
}
|
}
|
||||||
|
|
@ -1166,6 +1181,10 @@
|
||||||
.border-collapse {
|
.border-collapse {
|
||||||
border-collapse: collapse;
|
border-collapse: collapse;
|
||||||
}
|
}
|
||||||
|
.-translate-x-1 {
|
||||||
|
--tw-translate-x: calc(var(--spacing) * -1);
|
||||||
|
translate: var(--tw-translate-x) var(--tw-translate-y);
|
||||||
|
}
|
||||||
.-translate-x-1\/2 {
|
.-translate-x-1\/2 {
|
||||||
--tw-translate-x: calc(calc(1/2 * 100%) * -1);
|
--tw-translate-x: calc(calc(1/2 * 100%) * -1);
|
||||||
translate: var(--tw-translate-x) var(--tw-translate-y);
|
translate: var(--tw-translate-x) var(--tw-translate-y);
|
||||||
|
|
@ -1178,6 +1197,10 @@
|
||||||
--tw-translate-x: calc(var(--spacing) * 16);
|
--tw-translate-x: calc(var(--spacing) * 16);
|
||||||
translate: var(--tw-translate-x) var(--tw-translate-y);
|
translate: var(--tw-translate-x) var(--tw-translate-y);
|
||||||
}
|
}
|
||||||
|
.-translate-y-1 {
|
||||||
|
--tw-translate-y: calc(var(--spacing) * -1);
|
||||||
|
translate: var(--tw-translate-x) var(--tw-translate-y);
|
||||||
|
}
|
||||||
.-translate-y-1\/2 {
|
.-translate-y-1\/2 {
|
||||||
--tw-translate-y: calc(calc(1/2 * 100%) * -1);
|
--tw-translate-y: calc(calc(1/2 * 100%) * -1);
|
||||||
translate: var(--tw-translate-x) var(--tw-translate-y);
|
translate: var(--tw-translate-x) var(--tw-translate-y);
|
||||||
|
|
@ -1304,6 +1327,13 @@
|
||||||
.gap-6 {
|
.gap-6 {
|
||||||
gap: calc(var(--spacing) * 6);
|
gap: calc(var(--spacing) * 6);
|
||||||
}
|
}
|
||||||
|
.space-y-0 {
|
||||||
|
:where(& > :not(:last-child)) {
|
||||||
|
--tw-space-y-reverse: 0;
|
||||||
|
margin-block-start: calc(calc(var(--spacing) * 0) * var(--tw-space-y-reverse));
|
||||||
|
margin-block-end: calc(calc(var(--spacing) * 0) * calc(1 - var(--tw-space-y-reverse)));
|
||||||
|
}
|
||||||
|
}
|
||||||
.space-y-0\.5 {
|
.space-y-0\.5 {
|
||||||
:where(& > :not(:last-child)) {
|
:where(& > :not(:last-child)) {
|
||||||
--tw-space-y-reverse: 0;
|
--tw-space-y-reverse: 0;
|
||||||
|
|
@ -1740,6 +1770,9 @@
|
||||||
.bg-blue-600 {
|
.bg-blue-600 {
|
||||||
background-color: var(--color-blue-600);
|
background-color: var(--color-blue-600);
|
||||||
}
|
}
|
||||||
|
.bg-cyan-400 {
|
||||||
|
background-color: var(--color-cyan-400);
|
||||||
|
}
|
||||||
.bg-cyan-400\/10 {
|
.bg-cyan-400\/10 {
|
||||||
background-color: color-mix(in srgb, oklch(78.9% 0.154 211.53) 10%, transparent);
|
background-color: color-mix(in srgb, oklch(78.9% 0.154 211.53) 10%, transparent);
|
||||||
@supports (color: color-mix(in lab, red, red)) {
|
@supports (color: color-mix(in lab, red, red)) {
|
||||||
|
|
@ -1803,6 +1836,9 @@
|
||||||
.bg-indigo-300 {
|
.bg-indigo-300 {
|
||||||
background-color: var(--color-indigo-300);
|
background-color: var(--color-indigo-300);
|
||||||
}
|
}
|
||||||
|
.bg-lime-500 {
|
||||||
|
background-color: var(--color-lime-500);
|
||||||
|
}
|
||||||
.bg-lime-500\/15 {
|
.bg-lime-500\/15 {
|
||||||
background-color: color-mix(in srgb, oklch(76.8% 0.233 130.85) 15%, transparent);
|
background-color: color-mix(in srgb, oklch(76.8% 0.233 130.85) 15%, transparent);
|
||||||
@supports (color: color-mix(in lab, red, red)) {
|
@supports (color: color-mix(in lab, red, red)) {
|
||||||
|
|
@ -1824,6 +1860,9 @@
|
||||||
.bg-orange-500 {
|
.bg-orange-500 {
|
||||||
background-color: var(--color-orange-500);
|
background-color: var(--color-orange-500);
|
||||||
}
|
}
|
||||||
|
.bg-orange-700 {
|
||||||
|
background-color: var(--color-orange-700);
|
||||||
|
}
|
||||||
.bg-orange-700\/30 {
|
.bg-orange-700\/30 {
|
||||||
background-color: color-mix(in srgb, oklch(55.3% 0.195 38.402) 30%, transparent);
|
background-color: color-mix(in srgb, oklch(55.3% 0.195 38.402) 30%, transparent);
|
||||||
@supports (color: color-mix(in lab, red, red)) {
|
@supports (color: color-mix(in lab, red, red)) {
|
||||||
|
|
@ -1866,6 +1905,9 @@
|
||||||
.bg-slate-900 {
|
.bg-slate-900 {
|
||||||
background-color: var(--color-slate-900);
|
background-color: var(--color-slate-900);
|
||||||
}
|
}
|
||||||
|
.bg-slate-950 {
|
||||||
|
background-color: var(--color-slate-950);
|
||||||
|
}
|
||||||
.bg-slate-950\/60 {
|
.bg-slate-950\/60 {
|
||||||
background-color: color-mix(in srgb, oklch(12.9% 0.042 264.695) 60%, transparent);
|
background-color: color-mix(in srgb, oklch(12.9% 0.042 264.695) 60%, transparent);
|
||||||
@supports (color: color-mix(in lab, red, red)) {
|
@supports (color: color-mix(in lab, red, red)) {
|
||||||
|
|
@ -2837,6 +2879,12 @@
|
||||||
--tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 calc(4px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor);
|
--tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 calc(4px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor);
|
||||||
box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
|
box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
|
||||||
}
|
}
|
||||||
|
.shadow-black {
|
||||||
|
--tw-shadow-color: #000;
|
||||||
|
@supports (color: color-mix(in lab, red, red)) {
|
||||||
|
--tw-shadow-color: color-mix(in oklab, var(--color-black) var(--tw-shadow-alpha), transparent);
|
||||||
|
}
|
||||||
|
}
|
||||||
.shadow-black\/20 {
|
.shadow-black\/20 {
|
||||||
--tw-shadow-color: color-mix(in srgb, #000 20%, transparent);
|
--tw-shadow-color: color-mix(in srgb, #000 20%, transparent);
|
||||||
@supports (color: color-mix(in lab, red, red)) {
|
@supports (color: color-mix(in lab, red, red)) {
|
||||||
|
|
@ -2849,6 +2897,9 @@
|
||||||
--tw-shadow-color: color-mix(in oklab, var(--color-gray-200) var(--tw-shadow-alpha), transparent);
|
--tw-shadow-color: color-mix(in oklab, var(--color-gray-200) var(--tw-shadow-alpha), transparent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.ring-black {
|
||||||
|
--tw-ring-color: var(--color-black);
|
||||||
|
}
|
||||||
.ring-black\/5 {
|
.ring-black\/5 {
|
||||||
--tw-ring-color: color-mix(in srgb, #000 5%, transparent);
|
--tw-ring-color: color-mix(in srgb, #000 5%, transparent);
|
||||||
@supports (color: color-mix(in lab, red, red)) {
|
@supports (color: color-mix(in lab, red, red)) {
|
||||||
|
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 5.3 KiB After Width: | Height: | Size: 15 KiB |
Loading…
Reference in New Issue