Commit: b8d5c39
Parent: f66b760

Styling

Mårten Åsberg committed on 2026-05-05 at 15:55
MatDenDagen/Components/Layout/AdminLayout.razor +23 -0
diff --git a/MatDenDagen/Components/Layout/AdminLayout.razor b/MatDenDagen/Components/Layout/AdminLayout.razor
new file mode 100644
index 0000000..1d6c901
@@ -0,0 +1,23 @@
@inherits LayoutComponentBase
<div class="admin-layout">
<div class="admin-sidebar">
<div class="admin-logo">
<h2><a href="/admin">Admin Panel</a></h2>
</div>
<nav class="admin-nav">
<ul>
<li><a href="/admin/questions">Frågor</a></li>
<li><a href="/admin/participants">Deltagare</a></li>
<li><a href="/admin/date">Datum</a></li>
<li><a href="/admin/export">Exportera</a></li>
<li><a href="/admin/logout">Logga ut</a></li>
</ul>
</nav>
</div>
<div class="admin-main">
<main class="admin-content">
@Body
</main>
</div>
</div>
MatDenDagen/Components/Layout/AdminLayout.razor.css +130 -0
diff --git a/MatDenDagen/Components/Layout/AdminLayout.razor.css b/MatDenDagen/Components/Layout/AdminLayout.razor.css
new file mode 100644
index 0000000..6f5d836
@@ -0,0 +1,130 @@
.admin-layout {
display: flex;
min-height: 100vh;
background-color: var(--paper-color);
background-image: var(--paper-texture);
}
.admin-sidebar {
width: 250px;
background-color: var(--wood-medium);
color: var(--paper-color);
padding: 1.5em 0;
position: relative;
box-shadow: 2px 0 5px rgba(0, 0, 0, 0.1);
z-index: 100;
}
.admin-logo {
padding: 0 1.5em 1.5em;
border-bottom: 2px solid var(--wood-dark);
margin-bottom: 1.5em;
}
.admin-logo h2 {
font-size: 1.5rem;
text-align: center;
position: relative;
}
.admin-logo h2 a {
color: var(--paper-color);
text-decoration: none;
}
.admin-logo h2::after {
content: "";
position: absolute;
bottom: -10px;
left: 50%;
transform: translateX(-50%);
width: 60px;
height: 2px;
background: var(--paper-color);
}
.admin-nav ul {
list-style: none;
padding: 0;
margin: 0;
}
.admin-nav li {
margin: 0.5em 0;
}
.admin-nav a {
display: block;
padding: 0.8em 1.5em;
color: var(--paper-color);
text-decoration: none;
transition: var(--transition);
position: relative;
border-left: 3px solid transparent;
}
.admin-nav a:hover,
.admin-nav a.active {
background-color: rgba(255, 255, 255, 0.1);
border-left-color: var(--paper-color);
}
.admin-nav a::before {
content: "•";
margin-right: 0.8em;
color: var(--paper-color);
opacity: 0.6;
}
.admin-main {
flex: 1;
padding: 2em;
background-color: rgba(245, 240, 230, 0.5);
}
.admin-content {
max-width: 1200px;
margin: 0 auto;
background-color: var(--paper-dark);
border: 1px solid var(--wood-medium);
border-radius: var(--border-radius);
padding: 2em;
box-shadow: var(--box-shadow);
position: relative;
}
.admin-content::before {
content: "";
position: absolute;
top: 0;
left: 0;
right: 0;
height: 15px;
background: var(--stitching-pattern);
background-size: 8px 1px;
opacity: 0.3;
}
@media (max-width: 768px) {
.admin-layout {
flex-direction: column;
}
.admin-sidebar {
width: 100%;
padding: 1em 0;
}
.admin-nav {
display: flex;
overflow-x: auto;
}
.admin-nav li {
white-space: nowrap;
}
.admin-main {
padding: 1em;
}
}
MatDenDagen/Components/Layout/MainLayout.razor +19 -1
diff --git a/MatDenDagen/Components/Layout/MainLayout.razor b/MatDenDagen/Components/Layout/MainLayout.razor
index e1a9a75..604eb68 100644
@@ -1,3 +1,21 @@
@inherits LayoutComponentBase
@Body
<div class="main-layout">
<header class="main-header">
<div class="container">
<h1 class="site-title"><a href="/">Mat den <span class="highlight">dagen</span></a></h1>
</div>
</header>
<main class="main-content">
<div class="container">
@Body
</div>
</main>
<footer class="main-footer">
<div class="container">
<p>Våran kokbok</p>
</div>
</footer>
</div>
MatDenDagen/Components/Layout/MainLayout.razor.css +44 -0
diff --git a/MatDenDagen/Components/Layout/MainLayout.razor.css b/MatDenDagen/Components/Layout/MainLayout.razor.css
index e69de29..b078cc9 100644
@@ -0,0 +1,44 @@
.main-layout {
display: flex;
flex-direction: column;
min-height: 100vh;
background-color: var(--paper-color);
background-image: var(--paper-texture);
}
.main-header {
background-color: var(--paper-color);
border-bottom: 2px solid var(--wood-medium);
padding: 1.5em 0;
box-shadow: var(--box-shadow);
}
.site-title {
font-family: var(--font-heading);
font-size: 2.5rem;
text-align: center;
position: relative;
}
.site-title a {
color: var(--ink-dark);
text-decoration: none;
}
.main-content {
flex: 1;
padding: 2em 0;
}
.main-footer {
background-color: var(--paper-color);
border-top: 2px solid var(--wood-medium);
padding: 1.5em 0;
text-align: center;
box-shadow: 0 -2px 4px rgba(0, 0, 0, 0.1);
}
.main-footer p {
color: var(--ink-medium);
font-style: italic;
}
MatDenDagen/Components/Pages/Admin/Date.razor +1 -0
diff --git a/MatDenDagen/Components/Pages/Admin/Date.razor b/MatDenDagen/Components/Pages/Admin/Date.razor
index 03adce0..05062ab 100644
@@ -7,6 +7,7 @@
@using Microsoft.AspNetCore.Components
@inject DateService dateService
@inject QuestionnaireContext questionnaireContext
@layout AdminLayout
<h2>Konfigurera datum</h2>
MatDenDagen/Components/Pages/Admin/Date.razor.css +80 -0
diff --git a/MatDenDagen/Components/Pages/Admin/Date.razor.css b/MatDenDagen/Components/Pages/Admin/Date.razor.css
new file mode 100644
index 0000000..b6e65fd
@@ -0,0 +1,80 @@
h2 {
color: var(--wood-dark);
margin-bottom: 1.5em;
padding-bottom: 0.5em;
border-bottom: 2px solid var(--wood-medium);
}
.EditForm {
background-color: var(--paper-color);
border: 1px solid var(--wood-medium);
border-radius: var(--border-radius);
padding: 1.5em;
margin: 1.5em 0;
}
label {
display: block;
margin: 1em 0;
}
label span {
display: block;
margin-bottom: 0.5em;
font-weight: 600;
}
input[type="date"] {
padding: 0.6em;
width: auto;
}
input[type="submit"],
button {
background-color: var(--wood-light);
background-image: repeating-linear-gradient(
45deg,
var(--wood-medium),
var(--wood-medium) 20px,
var(--wood-dark) 20px,
var(--wood-dark) 40px
);
color: var(--paper-color);
border: 2px solid var(--wood-dark);
padding: 0.6em 1.2em;
border-radius: var(--border-radius);
cursor: pointer;
font-weight: 600;
transition:
transform 0.3s ease,
box-shadow 0.3s ease,
opacity 0.3s ease;
box-shadow: var(--box-shadow);
background-size: 40px 40px;
opacity: 0.95;
}
input[type="submit"]:hover,
button:hover {
transform: translateY(-1px);
box-shadow: 0 3px 6px rgba(0, 0, 0, 0.15);
opacity: 1;
}
hr {
border: 0;
height: 2px;
background: var(--stitching-pattern);
background-size: 16px 2px;
margin: 2em 0;
}
.success-message {
color: var(--wood-medium);
font-weight: 600;
}
disabled {
opacity: 0.6;
cursor: not-allowed;
}
MatDenDagen/Components/Pages/Admin/Export.razor +1 -0
diff --git a/MatDenDagen/Components/Pages/Admin/Export.razor b/MatDenDagen/Components/Pages/Admin/Export.razor
index 23ecbaf..4a1a3db 100644
@@ -1,6 +1,7 @@
@page "/admin/export"
@using Microsoft.AspNetCore.Authorization
@attribute [Authorize(Roles = "Admin")]
@layout AdminLayout
<h1>Export</h1>
MatDenDagen/Components/Pages/Admin/Export.razor.css +38 -0
diff --git a/MatDenDagen/Components/Pages/Admin/Export.razor.css b/MatDenDagen/Components/Pages/Admin/Export.razor.css
new file mode 100644
index 0000000..571e80e
@@ -0,0 +1,38 @@
h1 {
color: var(--wood-dark);
margin-bottom: 1.5em;
padding-bottom: 0.5em;
border-bottom: 2px solid var(--wood-medium);
}
p {
font-size: 1.1em;
}
a {
display: inline-block;
padding: 0.6em 1.2em;
background-color: var(--wood-light);
background-image: repeating-linear-gradient(
45deg,
var(--wood-medium),
var(--wood-medium) 20px,
var(--wood-dark) 20px,
var(--wood-dark) 40px
);
color: var(--paper-color);
border: 2px solid var(--wood-dark);
border-radius: var(--border-radius);
font-weight: 600;
transition: var(--transition);
box-shadow: var(--box-shadow);
background-size: 40px 40px;
opacity: 0.95;
text-decoration: none;
}
a:hover {
transform: translateY(-1px);
box-shadow: 0 3px 6px rgba(0, 0, 0, 0.15);
opacity: 1;
}
MatDenDagen/Components/Pages/Admin/Index.razor +49 -0
diff --git a/MatDenDagen/Components/Pages/Admin/Index.razor b/MatDenDagen/Components/Pages/Admin/Index.razor
new file mode 100644
index 0000000..21a25bf
@@ -0,0 +1,49 @@
@page "/admin"
@page "/admin/index"
@attribute [Authorize(Roles = "Admin")]
@layout AdminLayout
@using Microsoft.AspNetCore.Authorization
@using MatDenDagen.Infrastructure.Storage.Database
@using Microsoft.EntityFrameworkCore
@inject QuestionnaireContext questionnaireContext
<div class="admin-dashboard">
<h1>Admin Dashboard</h1>
<div class="welcome-card card">
<h2>Välkommen till administrationspanelen</h2>
<p>Här kan du hantera alla aspekter av Mat den Dagen-upplevelsen.</p>
<p>Använd menyn till vänster för att navigera mellan olika funktioner.</p>
</div>
<div class="quick-stats">
<h3>Snabbstatistik</h3>
<div class="stats-grid">
<div class="stat-card card">
<h4>Frågor</h4>
<p class="stat-number">@questionCount</p>
</div>
<div class="stat-card card">
<h4>Deltagare</h4>
<p class="stat-number">@participantCount</p>
</div>
<div class="stat-card card">
<h4>Inskickade svar</h4>
<p class="stat-number">@submissionCount</p>
</div>
</div>
</div>
</div>
@code {
private int questionCount = 0;
private int participantCount = 0;
private int submissionCount = 0;
protected override async Task OnInitializedAsync()
{
questionCount = await questionnaireContext.Questions.CountAsync();
participantCount = await questionnaireContext.Participants.CountAsync();
submissionCount = await questionnaireContext.Submissions.CountAsync();
}
}
MatDenDagen/Components/Pages/Admin/Index.razor.css +74 -0
diff --git a/MatDenDagen/Components/Pages/Admin/Index.razor.css b/MatDenDagen/Components/Pages/Admin/Index.razor.css
new file mode 100644
index 0000000..26f0b2f
@@ -0,0 +1,74 @@
.admin-dashboard {
max-width: 1000px;
margin: 0 auto;
}
.admin-dashboard h1 {
color: var(--wood-dark);
margin-bottom: 1em;
padding-bottom: 0.5em;
border-bottom: 2px solid var(--wood-medium);
}
.welcome-card {
margin: 2em 0;
text-align: center;
padding: 2em;
}
.welcome-card h2 {
color: var(--wood-medium);
margin-bottom: 1em;
}
.welcome-card p {
font-size: 1.1em;
line-height: 1.8;
color: var(--ink-medium);
}
.quick-stats {
margin: 3em 0;
}
.quick-stats h3 {
color: var(--wood-medium);
margin-bottom: 1.5em;
font-size: 1.3em;
}
.stats-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 1.5em;
margin-top: 1.5em;
}
.stat-card {
text-align: center;
padding: 1.5em;
position: relative;
}
.stat-card h4 {
color: var(--wood-dark);
margin-bottom: 0.5em;
font-size: 1.1em;
}
.stat-number {
font-size: 2.5em;
font-weight: 600;
color: var(--wood-medium);
margin: 0;
}
@media (max-width: 768px) {
.stats-grid {
grid-template-columns: 1fr;
}
.welcome-card {
padding: 1.5em;
}
}
MatDenDagen/Components/Pages/Admin/Login.razor +50 -12
diff --git a/MatDenDagen/Components/Pages/Admin/Login.razor b/MatDenDagen/Components/Pages/Admin/Login.razor
index 3995f61..686a2f0 100644
@@ -4,6 +4,7 @@
@using Microsoft.AspNetCore.Components.Authorization
@inject AdminAuthService AdminAuthService
@inject NavigationManager NavigationManager
@layout AdminLayout
<h1>Admin Login</h1>
@@ -52,7 +53,7 @@
}
await AdminAuthService.SignIn();
NavigationManager.NavigateTo("/admin/questions", true);
NavigationManager.NavigateTo("/admin", true);
}
private sealed class Model
@@ -63,35 +64,72 @@
<style>
.error {
color: red;
color: #c00;
margin-bottom: 1rem;
padding: 1em;
background-color: rgba(200, 100, 100, 0.1);
border-left: 4px solid #c00;
}
form {
max-width: 400px;
margin: 0 auto;
EditForm {
max-width: 500px;
margin: 2em auto;
background-color: rgba(245, 240, 230, 0.8);
border: 1px solid var(--wood-medium);
border-radius: var(--border-radius);
padding: 2em;
position: relative;
}
div {
EditForm::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 30px;
background: var(--stitching-pattern);
background-size: 16px 2px;
}
p {
margin-bottom: 1rem;
}
label {
display: block;
margin-bottom: 0.5rem;
font-weight: 600;
}
input {
width: 100%;
padding: 0.5rem;
box-sizing: border-box;
padding: 0.8em;
border: 2px solid var(--wood-medium);
border-radius: var(--border-radius);
background-color: rgba(245, 240, 230, 0.8);
font-family: var(--font-main);
transition: var(--transition);
}
button {
padding: 0.5rem 1rem;
background-color: #007bff;
color: white;
border: none;
padding: 0.6rem 1.2rem;
background-color: var(--wood-light);
background-image: repeating-linear-gradient(45deg, var(--wood-medium), var(--wood-medium) 20px, var(--wood-dark) 20px, var(--wood-dark) 40px);
color: var(--paper-color);
border: 2px solid var(--wood-dark);
border-radius: var(--border-radius);
cursor: pointer;
font-weight: 600;
transition: var(--transition);
box-shadow: var(--box-shadow);
background-size: 40px 40px;
opacity: 0.95;
}
button:hover {
transform: translateY(-1px);
box-shadow: 0 3px 6px rgba(0, 0, 0, 0.15);
opacity: 1;
}
</style>
MatDenDagen/Components/Pages/Admin/Participants.razor +24 -23
diff --git a/MatDenDagen/Components/Pages/Admin/Participants.razor b/MatDenDagen/Components/Pages/Admin/Participants.razor
index d58dc13..f7e72aa 100644
@@ -6,6 +6,7 @@
@using Microsoft.EntityFrameworkCore
@inject TimeProvider timeProvider
@inject QuestionnaireContext questionnaireContext
@layout AdminLayout
<ul>
@foreach (var participant in questionnaireContext.Participants)
@@ -13,8 +14,7 @@
var updateFormName = "UpdateParticipant-" + participant.Id;
var removeFormName = "RemoveParticipant-" + participant.Id;
<li>
<EditForm FormName="@updateFormName" Model="@updateParticipantModel" OnSubmit="@UpdateParticipant"
Enhance>
<EditForm FormName="@updateFormName" Model="@updateParticipantModel" OnSubmit="@UpdateParticipant" Enhance>
<input type="hidden" name="updateParticipantModel.ParticipantId" value="@participant.Id.ToString()" />
<p>
<label>
@@ -31,15 +31,15 @@
<p>
<label>
<span>Notifikationsoffset (minuter från midnatt):</span>
<input type="number" name="updateParticipantModel.NotificationMinutesOffset" value="@participant.NotificationMinutesOffset" />
<input type="number" name="updateParticipantModel.NotificationMinutesOffset"
value="@participant.NotificationMinutesOffset" />
</label>
</p>
<p>
<input type="submit" value="Uppdatera" />
</p>
</EditForm>
<EditForm FormName="@removeFormName" Model="@removeParticipantModel" OnSubmit="@RemoveParticipant"
Enhance>
<EditForm FormName="@removeFormName" Model="@removeParticipantModel" OnSubmit="@RemoveParticipant" Enhance>
<input type="hidden" name="removeParticipantModel.ParticipantId" value="@participant.Id.ToString()" />
<input type="submit" value="Ta bort" />
</EditForm>
@@ -56,21 +56,21 @@
<input type="text" name="addParticipantModel.Name" required />
</label>
</p>
<p>
<label>
<span>Telefonnummer:</span>
<input type="text" name="addParticipantModel.PhoneNumber" required />
</label>
</p>
<p>
<label>
<span>Notifikationsoffset (minuter från midnatt):</span>
<input type="number" name="addParticipantModel.NotificationMinutesOffset" />
</label>
</p>
<p>
<input type="submit" value="Lägg till" />
</p>
<p>
<label>
<span>Telefonnummer:</span>
<input type="text" name="addParticipantModel.PhoneNumber" required />
</label>
</p>
<p>
<label>
<span>Notifikationsoffset (minuter från midnatt):</span>
<input type="number" name="addParticipantModel.NotificationMinutesOffset" />
</label>
</p>
<p>
<input type="submit" value="Lägg till" />
</p>
</EditForm>
@code {
@@ -96,9 +96,10 @@
{
return;
}
var participant = new Participant {
Id = Guid.CreateVersion7(timeProvider.GetUtcNow()),
Name = name,
var participant = new Participant
{
Id = Guid.CreateVersion7(timeProvider.GetUtcNow()),
Name = name,
PhoneNumber = phoneNumber,
NotificationMinutesOffset = addParticipantModel?.NotificationMinutesOffset
};
MatDenDagen/Components/Pages/Admin/Participants.razor.css +71 -0
diff --git a/MatDenDagen/Components/Pages/Admin/Participants.razor.css b/MatDenDagen/Components/Pages/Admin/Participants.razor.css
new file mode 100644
index 0000000..323471a
@@ -0,0 +1,71 @@
ul {
list-style: none;
}
li {
background-color: var(--paper-color);
border: 1px solid var(--wood-medium);
border-radius: var(--border-radius);
padding: 1.5em;
margin: 1.5em 0;
}
.EditForm {
margin: 1em 0;
}
label {
display: block;
margin: 0.8em 0;
}
label span {
display: block;
margin-bottom: 0.3em;
font-weight: 600;
}
input[type="text"],
input[type="number"] {
padding: 0.5em;
width: 100%;
max-width: 300px;
}
input[type="submit"] {
background-color: var(--wood-light);
background-image: repeating-linear-gradient(
45deg,
var(--wood-medium),
var(--wood-medium) 20px,
var(--wood-dark) 20px,
var(--wood-dark) 40px
);
color: var(--paper-color);
border: 2px solid var(--wood-dark);
padding: 0.4em 0.8em;
border-radius: var(--border-radius);
cursor: pointer;
font-weight: 600;
transition:
transform 0.3s ease,
box-shadow 0.3s ease,
opacity 0.3s ease;
box-shadow: var(--box-shadow);
background-size: 40px 40px;
opacity: 0.95;
}
input[type="submit"]:hover {
transform: translateY(-1px);
box-shadow: 0 3px 6px rgba(0, 0, 0, 0.15);
opacity: 1;
}
hr {
border: 0;
height: 2px;
background: var(--stitching-pattern);
background-size: 16px 2px;
margin: 2em 0;
}
MatDenDagen/Components/Pages/Admin/Questions.razor +1 -0
diff --git a/MatDenDagen/Components/Pages/Admin/Questions.razor b/MatDenDagen/Components/Pages/Admin/Questions.razor
index 2f49943..f15816e 100644
@@ -6,6 +6,7 @@
@using Microsoft.EntityFrameworkCore
@inject TimeProvider timeProvider
@inject QuestionnaireContext questionnaireContext
@layout AdminLayout
<ul>
@foreach (var question in questionnaireContext.Questions)
MatDenDagen/Components/Pages/Admin/Questions.razor.css +73 -0
diff --git a/MatDenDagen/Components/Pages/Admin/Questions.razor.css b/MatDenDagen/Components/Pages/Admin/Questions.razor.css
new file mode 100644
index 0000000..7f88ff7
@@ -0,0 +1,73 @@
ul {
list-style: none;
}
li {
display: flex;
justify-content: space-between;
align-items: center;
background-color: var(--paper-color);
border: 1px solid var(--wood-medium);
border-radius: var(--border-radius);
padding: 1em 1.5em;
margin: 1em 0;
}
span {
font-weight: 500;
}
.EditForm {
margin-left: 1em;
}
input[type="submit"] {
background-color: var(--wood-light);
background-image: repeating-linear-gradient(
45deg,
var(--wood-medium),
var(--wood-medium) 20px,
var(--wood-dark) 20px,
var(--wood-dark) 40px
);
color: var(--paper-color);
border: 2px solid var(--wood-dark);
padding: 0.4em 0.8em;
border-radius: var(--border-radius);
cursor: pointer;
font-weight: 600;
transition:
transform 0.3s ease,
box-shadow 0.3s ease,
opacity 0.3s ease;
box-shadow: var(--box-shadow);
background-size: 40px 40px;
opacity: 0.95;
}
input[type="submit"]:hover {
transform: translateY(-1px);
box-shadow: 0 3px 6px rgba(0, 0, 0, 0.15);
opacity: 1;
}
hr {
border: 0;
height: 2px;
background: var(--stitching-pattern);
background-size: 16px 2px;
margin: 2em 0;
}
.EditForm input[type="text"] {
padding: 0.5em;
width: 100%;
max-width: 400px;
}
.EditForm input[type="submit"] {
background-color: var(--wood-light);
background-image: var(--wood-grain);
color: var(--paper-color);
border: 2px solid var(--wood-dark);
}
MatDenDagen/Components/Pages/Home.razor +43 -17
diff --git a/MatDenDagen/Components/Pages/Home.razor b/MatDenDagen/Components/Pages/Home.razor
index f0569d3..ab7de6a 100644
@@ -2,30 +2,56 @@
@using MatDenDagen.Models
@using MatDenDagen.Services
@inject DateService dateService
@layout MainLayout
<h1>Välkommen till Mat den <strong>dagen</strong></h1>
<div class="home-content">
<h1>Välkommen till Mat den <span class="highlight">dagen</span></h1>
<div class="cookbook-intro card">
<p>Vi delar recept och matupplevelser från en och samma dag.</p>
</div>
@if (dateSpan is null)
{
<p>Ingen datumspan är konfigurerad ännu. Kontakta administratören.</p>
}
else
{
<h2>Datumspan</h2>
<p><strong>Dagen</strong> kommer att inträffa någon gång mellan:</p>
<p>
<code>@dateSpan.Start</code> och <code>@dateSpan.End</code>
</p>
@if (actualDate is null)
@if (dateSpan is null)
{
<p>Datumet för "the day" har inte slumpats ännu.</p>
<div class="card">
<p>Ingen datumspan är konfigurerad ännu. Kontakta administratören.</p>
</div>
}
else
{
<h2><strong>Dagen</strong> är den <code>@actualDate</code></h2>
<div class="date-section card">
<h2>Datumspan</h2>
<p><span class="highlight">Dagen</span> kommer att inträffa någon gång mellan:</p>
<div class="date-range">
<span class="date-start"><code>@dateSpan.Start</code></span>
<span class="date-separator">och</span>
<span class="date-end"><code>@dateSpan.End</code></span>
</div>
@if (actualDate is null)
{
<p>Datumet för "the day" har inte slumpats ännu.</p>
}
else
{
<div class="date-reveal-container">
<input type="checkbox" id="date-reveal-checkbox" class="date-reveal-checkbox" style="display: none;">
<label for="date-reveal-checkbox" class="date-reveal-toggle">
<span class="reveal-button">Avslöja dagen</span>
</label>
<div class="date-reveal-content">
<h2><span class="highlight">Dagen</span> är den <code>@actualDate</code></h2>
<p>Spara datumet i din almanacka och se fram emot en underbar matupplevelse!</p>
</div>
</div>
}
</div>
}
}
<div class="action-buttons">
<a href="/submission" class="btn">Skicka in din mat</a>
</div>
</div>
@code {
private DateSpan? dateSpan { get; set; }
MatDenDagen/Components/Pages/Home.razor.css +131 -0
diff --git a/MatDenDagen/Components/Pages/Home.razor.css b/MatDenDagen/Components/Pages/Home.razor.css
new file mode 100644
index 0000000..541b4b0
@@ -0,0 +1,131 @@
.home-content {
max-width: 800px;
margin: 0 auto;
}
.cookbook-intro {
margin-bottom: 2em;
text-align: center;
font-size: 1.1em;
line-height: 1.8;
color: var(--ink-medium);
border: 1px solid var(--wood-medium);
border-radius: var(--border-radius);
padding: 1.5em;
position: relative;
}
.date-section {
margin: 2em 0;
text-align: center;
border: 1px solid var(--wood-medium);
border-radius: var(--border-radius);
padding: 1.5em;
position: relative;
}
.date-range {
display: flex;
justify-content: center;
align-items: center;
gap: 1em;
margin: 1.5em 0;
flex-wrap: wrap;
}
.date-start,
.date-end {
font-size: 1.3em;
padding: 0.5em 1em;
background-color: rgba(212, 167, 106, 0.2);
border: 1px dashed var(--stitching);
border-radius: 20px;
}
.date-separator {
font-style: italic;
color: var(--ink-medium);
}
.date-reveal-container {
margin-top: 2em;
}
.date-reveal-toggle {
display: inline-block;
cursor: pointer;
}
.reveal-button {
display: inline-block;
padding: 0.6em 1.2em;
background-color: var(--wood-light);
background-image: repeating-linear-gradient(
45deg,
var(--wood-medium),
var(--wood-medium) 20px,
var(--wood-dark) 20px,
var(--wood-dark) 40px
);
color: var(--paper-color);
border: 2px solid var(--wood-dark);
border-radius: 20px;
font-weight: 600;
transition:
transform 0.3s ease,
box-shadow 0.3s ease,
opacity 0.3s ease;
box-shadow: var(--box-shadow);
background-size: 40px 40px;
opacity: 0.95;
cursor: pointer;
}
.reveal-button:hover {
transform: translateY(-1px);
box-shadow: 0 3px 6px rgba(0, 0, 0, 0.15);
opacity: 1;
}
.reveal-button:hover {
background-color: var(--wood-medium);
transform: translateY(-1px);
}
.date-reveal-content {
display: none;
margin-top: 1.5em;
padding: 1.5em;
background-color: var(--paper-color);
border: 1px solid var(--wood-medium);
border-radius: var(--border-radius);
box-shadow: var(--box-shadow);
animation: fadeIn 0.5s ease;
position: relative;
}
.date-reveal-checkbox:checked + label + .date-reveal-content,
.date-reveal-checkbox:checked ~ .date-reveal-content {
display: block;
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(-10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.action-buttons {
text-align: center;
margin: 2em 0;
}
.action-buttons .btn {
font-size: 1.1em;
padding: 0.8em 1.6em;
}
MatDenDagen/Components/Pages/NotFound.razor +8 -1
diff --git a/MatDenDagen/Components/Pages/NotFound.razor b/MatDenDagen/Components/Pages/NotFound.razor
index 6cf44be..04ef402 100644
@@ -1,3 +1,10 @@
@page "/not-found"
@layout MainLayout
<p>Sidan hittades inte</p>
<div class="not-found-content">
<h1>404 - Sidan hittades inte</h1>
<div class="card">
<p>Oj då! Den sidan du letar efter verkar ha gått vilse, precis som en glömd receptbok i köket.</p>
<p><a href="/" class="btn">Tillbaka till startsidan</a></p>
</div>
</div>
MatDenDagen/Components/Pages/NotFound.razor.css +14 -0
diff --git a/MatDenDagen/Components/Pages/NotFound.razor.css b/MatDenDagen/Components/Pages/NotFound.razor.css
new file mode 100644
index 0000000..edad7ff
@@ -0,0 +1,14 @@
.not-found-content {
max-width: 600px;
margin: 2em auto;
text-align: center;
}
.card {
margin-top: 1em;
text-align: center;
}
.btn {
margin-top: 1em;
}
MatDenDagen/Components/Pages/Submission.razor +63 -42
diff --git a/MatDenDagen/Components/Pages/Submission.razor b/MatDenDagen/Components/Pages/Submission.razor
index 7c27cd6..264305c 100644
@@ -8,54 +8,75 @@
@inject BlobStorageService blobService
@inject QuestionnaireContext questionnaireContext
@inject NavigationManager navigationManager
@layout MainLayout
<h1>Skicka in svar</h1>
<div class="submission-page">
<h1>Skicka in ditt svar</h1>
<div class="cookbook-intro card">
<p>Fyll i dina svar på frågorna nedan, skriv så mycket eller lite du vill på varje fråga, eller skippa frågor du
inte har något svar till.</p>
</div>
@if (success)
{
<p>Ditt svar har skickats in.</p>
}
@if (success)
{
<div class="success-message card">
<h2>Tack för ditt svar!</h2>
<p>Ditt svar har skickats in och vi ser fram emot att planera en underbar matupplevelse tillsammans.</p>
</div>
}
@if (errorMessage is not null)
{
<div class="error">
<p>@errorMessage</p>
</div>
}
@if (errorMessage is not null)
{
<div class="error-message card">
<h3>Ett fel uppstod</h3>
<p>@errorMessage</p>
</div>
}
@if (questions.Count == 0)
{
<p>Inga frågor hittades.</p>
}
else
{
<form method="post" enctype="multipart/form-data" @formname="Submission">
@foreach (var question in questions)
@if (questions.Count == 0)
{
<p>
<label>
<span>@question.Text</span><br />
<textarea name="answer[@question.Id]"></textarea>
</label>
</p>
<div class="card">
<p>Inga frågor hittades. Var god kontakta administratören.</p>
</div>
}
<p>
<label>
<span>Telefonnummer:</span><br />
<input type="text" name="phoneNumber" required placeholder="072XXXXXXX" />
</label>
</p>
<p>
<label>
<span>Bilagor</span><br />
<input type="file" name="files" multiple />
</label>
</p>
<p>
<button type="submit">Skicka</button>
</p>
</form>
}
else
{
<form method="post" enctype="multipart/form-data" @formname="Submission" class="submission-form">
<div class="form-group card">
<label>
<span class="form-label">Telefonnummer (obligatoriskt)</span>
<input type="text" name="phoneNumber" required placeholder="072XXXXXXX" class="form-input" />
</label>
</div>
<div class="form-questions">
@foreach (var question in questions)
{
<div class="form-group card">
<label>
<span class="question-text">@question.Text</span>
<textarea name="answer[@question.Id]" class="form-textarea" placeholder="Ditt svar här..."></textarea>
</label>
</div>
}
</div>
<div class="form-group card">
<span class="form-label">Bilagor</span>
<label class="file-upload-button">
<input type="file" name="files" multiple class="form-file-hidden" />
<span class="btn file-upload-text">Välj filer att ladda upp</span>
</label>
<p class="form-hint">Du kan ladda upp bilder eller dokument som kan vara relevanta.</p>
</div>
<div class="form-actions">
<button type="submit" class="btn submit-btn">Skicka in svar</button>
</div>
</form>
}
</div>
@code {
[CascadingParameter]
MatDenDagen/Components/Pages/Submission.razor.css +160 -0
diff --git a/MatDenDagen/Components/Pages/Submission.razor.css b/MatDenDagen/Components/Pages/Submission.razor.css
new file mode 100644
index 0000000..5bf2635
@@ -0,0 +1,160 @@
.submission-page {
max-width: 900px;
margin: 0 auto;
}
.cookbook-intro {
margin-bottom: 2em;
text-align: center;
font-size: 1.1em;
line-height: 1.8;
color: var(--ink-medium);
}
.success-message {
background-color: rgba(212, 167, 106, 0.1);
border-left: 4px solid var(--wood-medium);
text-align: center;
}
.success-message h2 {
color: var(--wood-medium);
}
.error-message {
background-color: rgba(200, 100, 100, 0.1);
border-left: 4px solid #c00;
color: #c00;
}
.submission-form {
margin-top: 2em;
}
.form-questions {
margin-bottom: 2em;
}
.form-group {
margin: 1.5em 0;
padding: 1.5em;
}
.question-text {
display: block;
margin-bottom: 1em;
font-weight: 600;
font-size: 1.1em;
color: var(--ink-dark);
}
.form-label {
display: block;
margin-bottom: 0.5em;
font-weight: 600;
}
.form-input,
.form-textarea,
.form-file {
width: 100%;
padding: 0.8em;
border: 2px solid var(--wood-medium);
border-radius: var(--border-radius);
background-color: rgba(245, 240, 230, 0.8);
font-family: var(--font-main);
transition: var(--transition);
}
.form-textarea {
min-height: 120px;
resize: vertical;
font-size: 1em;
}
.form-hint {
font-size: 0.9em;
color: var(--ink-medium);
margin-top: 0.5em;
font-style: italic;
}
/* File Upload Button Styling */
.file-upload-button {
display: inline-block;
margin: 0.5em 0;
}
.form-file-hidden {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
.file-upload-text {
position: relative;
display: inline-block;
padding: 0.6em 1.2em;
background-color: var(--wood-light);
background-image: repeating-linear-gradient(45deg, var(--wood-medium), var(--wood-medium) 20px, var(--wood-dark) 20px, var(--wood-dark) 40px);
color: var(--paper-color);
border: 2px solid var(--wood-dark);
border-radius: var(--border-radius);
font-weight: 600;
transition: transform 0.3s ease, box-shadow 0.3s ease, opacity 0.3s ease;
box-shadow: var(--box-shadow);
background-size: 40px 40px;
opacity: 0.95;
cursor: pointer;
}
.file-upload-text:hover {
transform: translateY(-1px);
box-shadow: 0 3px 6px rgba(0, 0, 0, 0.15);
opacity: 1;
}
.file-upload-text::after {
content: '📎';
margin-left: 0.5em;
font-size: 0.9em;
}
.form-actions {
text-align: center;
margin: 2em 0;
}
.submit-btn {
font-size: 1.1em;
padding: 0.8em 1.8em;
background-color: var(--wood-light);
background-image: repeating-linear-gradient(
45deg,
var(--wood-medium),
var(--wood-medium) 20px,
var(--wood-dark) 20px,
var(--wood-dark) 40px
);
color: var(--paper-color);
border: 2px solid var(--wood-dark);
border-radius: var(--border-radius);
font-weight: 600;
transition: var(--transition);
box-shadow: var(--box-shadow);
background-size: 40px 40px;
opacity: 0.95;
cursor: pointer;
}
.submit-btn:hover {
transform: translateY(-1px);
box-shadow: 0 3px 6px rgba(0, 0, 0, 0.15);
opacity: 1;
}
MatDenDagen/wwwroot/app.css +463 -0
diff --git a/MatDenDagen/wwwroot/app.css b/MatDenDagen/wwwroot/app.css
index e69de29..a8f20e0 100644
@@ -0,0 +1,463 @@
:root {
/* Cozy Cookbook Color Palette */
--paper-color: #f5f0e6;
--paper-dark: #e8d5b5;
--wood-light: #d4a76a;
--wood-medium: #8b7355;
--wood-dark: #5d4037;
--ink-dark: #3a2c1a;
--ink-medium: #6b5b47;
--ink-light: #9c8a7a;
--stitching: #a67c52;
/* Typography */
--font-main: Georgia, "Times New Roman", Times, serif;
--font-heading: Georgia, "Times New Roman", Times, serif;
--font-monospace: "Courier New", Courier, monospace;
/* Spacing & Sizing */
--base-spacing: 1rem;
--border-radius: 4px;
--box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
--transition: all 0.3s ease;
/* Skeuomorphic Effects - More subtle */
--paper-texture: linear-gradient(
90deg,
rgba(255, 255, 255, 0.05) 0%,
rgba(0, 0, 0, 0.01) 50%,
rgba(255, 255, 255, 0.05) 100%
);
--wood-grain: repeating-linear-gradient(
45deg,
var(--wood-medium),
var(--wood-medium) 15px,
var(--wood-dark) 15px,
var(--wood-dark) 30px
);
--stitching-pattern: repeating-linear-gradient(
to right,
var(--stitching) 0,
var(--stitching) 4px,
transparent 4px,
transparent 8px
);
}
.highlight {
color: var(--wood-medium);
text-decoration: underline;
text-decoration-style: wavy;
text-decoration-color: var(--stitching);
}
/* Base Reset & Typography */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
html {
font-size: 16px;
line-height: 1.6;
scroll-behavior: smooth;
}
body {
font-family: var(--font-main);
color: var(--ink-dark);
background-color: var(--paper-color);
min-height: 100vh;
position: relative;
line-height: 1.7;
}
/* Base Typography */
h1,
h2,
h3,
h4,
h5,
h6 {
font-family: var(--font-heading);
font-weight: 700;
line-height: 1.2;
margin-bottom: 0.5em;
color: var(--ink-dark);
}
h1 {
font-size: 2.2rem;
margin-bottom: 0.8em;
}
h2 {
font-size: 1.8rem;
margin-bottom: 0.7em;
}
h3 {
font-size: 1.5rem;
margin-bottom: 0.6em;
}
h4 {
font-size: 1.2rem;
margin-bottom: 0.5em;
}
p {
margin-bottom: 1.2em;
font-size: 1.1rem;
line-height: 1.8;
}
a {
color: var(--wood-medium);
text-decoration: none;
transition: var(--transition);
}
a:hover {
color: var(--wood-dark);
text-decoration: underline;
}
code {
font-family: var(--font-monospace);
background-color: rgba(212, 167, 106, 0.2);
padding: 0.2em 0.4em;
border-radius: var(--border-radius);
border: 1px dashed var(--stitching);
}
/* Utility Classes */
.container {
max-width: 1200px;
margin: 0 auto;
padding: 0 var(--base-spacing);
}
.btn {
display: inline-block;
padding: 0.6em 1.2em;
background-color: var(--wood-light);
background-image: repeating-linear-gradient(
45deg,
var(--wood-medium),
var(--wood-medium) 20px,
var(--wood-dark) 20px,
var(--wood-dark) 40px
);
color: var(--paper-color);
border: 2px solid var(--wood-dark);
border-radius: var(--border-radius);
font-family: var(--font-main);
font-weight: 600;
cursor: pointer;
transition:
transform 0.3s ease,
box-shadow 0.3s ease,
opacity 0.3s ease;
box-shadow: var(--box-shadow);
position: relative;
background-size: 40px 40px;
opacity: 0.95;
}
a.btn {
color: var(--paper-color);
text-decoration: none;
}
.btn:hover {
transform: translateY(-1px);
box-shadow: 0 3px 6px rgba(0, 0, 0, 0.15);
opacity: 1;
}
.btn:active {
transform: translateY(0);
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
}
/* Form Elements */
input[type="text"],
input[type="password"],
input[type="email"],
input[type="number"],
input[type="date"],
textarea,
select {
width: 100%;
padding: 0.8em;
margin: 0.5em 0;
border: 2px solid var(--wood-medium);
border-radius: var(--border-radius);
background-color: var(--paper-color);
font-family: var(--font-main);
font-size: 1em;
transition: var(--transition);
box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.05);
}
input:focus,
textarea:focus,
select:focus {
outline: none;
border-color: var(--wood-dark);
box-shadow:
inset 0 1px 3px rgba(0, 0, 0, 0.1),
0 0 0 2px rgba(212, 167, 106, 0.3);
}
label {
display: block;
margin-bottom: 0.5em;
font-weight: 600;
color: var(--ink-dark);
}
/* Card/Container Styles */
.card {
background-color: var(--paper-color);
border: 1px solid var(--wood-medium);
border-radius: var(--border-radius);
padding: 1.8em;
margin: 1.5em 0;
box-shadow: var(--box-shadow);
position: relative;
}
/* Date Reveal Functionality */
.date-reveal-container {
position: relative;
}
.date-reveal-toggle {
display: inline-block;
padding: 0.4em 0.8em;
}
.date-reveal-content {
display: none;
margin-top: 1em;
padding: 1em;
background-color: rgba(245, 240, 230, 0.8);
border: 1px dashed var(--stitching);
border-radius: var(--border-radius);
}
.date-reveal-checkbox:checked + .date-reveal-content {
display: block;
}
/* Animation Effects */
@keyframes subtlePulse {
0%,
100% {
transform: scale(1);
}
50% {
transform: scale(1.01);
}
}
.pulse-subtle {
animation: subtlePulse 8s ease-in-out infinite;
}
/* Responsive Design */
@media (max-width: 768px) {
.container {
padding: 0 0.75em;
}
h1 {
font-size: 1.8rem;
}
h2 {
font-size: 1.5rem;
}
}
/* Print Styles */
@media print {
body {
background: white;
color: black;
}
.no-print {
display: none;
}
}
/* Additional Utility Classes */
.text-center {
text-align: center;
}
.mt-1 {
margin-top: 0.5em;
}
.mt-2 {
margin-top: 1em;
}
.mt-3 {
margin-top: 1.5em;
}
.mt-4 {
margin-top: 2em;
}
.mb-1 {
margin-bottom: 0.5em;
}
.mb-2 {
margin-bottom: 1em;
}
.mb-3 {
margin-bottom: 1.5em;
}
.mb-4 {
margin-bottom: 2em;
}
.p-1 {
padding: 0.5em;
}
.p-2 {
padding: 1em;
}
.p-3 {
padding: 1.5em;
}
.p-4 {
padding: 2em;
}
/* Flex Utilities */
.flex {
display: flex;
}
.flex-wrap {
flex-wrap: wrap;
}
.justify-center {
justify-content: center;
}
.justify-between {
justify-content: space-between;
}
.items-center {
align-items: center;
}
.gap-1 {
gap: 0.5em;
}
.gap-2 {
gap: 1em;
}
.gap-3 {
gap: 1.5em;
}
.gap-4 {
gap: 2em;
}
/* Grid Utilities */
.grid {
display: grid;
}
.grid-cols-2 {
grid-template-columns: repeat(2, 1fr);
}
/* Animation for page transitions */
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.fade-in {
animation: fadeIn 0.3s ease-out;
}
/* Enhanced card styles with more skeuomorphic details */
.card-enhanced {
background-color: var(--paper-color);
border: 1px solid var(--wood-medium);
border-radius: var(--border-radius);
padding: 1.8em;
margin: 1.5em 0;
box-shadow: var(--box-shadow);
position: relative;
}
.card-enhanced::after {
content: "";
position: absolute;
top: 0;
right: 10px;
width: 25px;
height: 25px;
background: var(--paper-color);
transform: rotate(-45deg);
box-shadow: -1px -1px 2px rgba(0, 0, 0, 0.1);
border-left: 1px solid var(--wood-medium);
border-top: 1px solid var(--wood-medium);
z-index: 1;
}
/* Recipe book specific elements */
.recipe-title {
position: relative;
padding-bottom: 0.5em;
margin-bottom: 1em;
}
.recipe-title::after {
content: "";
position: absolute;
bottom: 0;
left: 0;
width: 60px;
height: 2px;
background: var(--wood-medium);
}
.ingredient-list {
list-style: none;
padding-left: 0;
}
.ingredient-list li {
padding: 0.3em 0;
position: relative;
padding-left: 1.5em;
}
.ingredient-list li::before {
content: "•";
position: absolute;
left: 0;
color: var(--wood-medium);
}
/* Final touches for consistency */
body {
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-rendering: optimizeLegibility;
}
README.md +2 -2
diff --git a/README.md b/README.md
index 1fb466b..5fbe95e 100644
@@ -2,9 +2,9 @@
## Features
- [ ] User pages
- [x] User pages
- [x] See the date of **the day**
- [ ] Hide the date by default
- [x] Hide the date by default
- [x] Submit questionnaire
- [x] Questionnaire only accepts phone numbers from configured participants
- [ ] Admin pages