This commit is contained in:
younes elhaddoury
2025-09-17 10:28:02 +02:00
parent 9c8fb9b205
commit bb13759af4
288 changed files with 102393 additions and 0 deletions

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,215 @@
{
"Version": 1,
"WorkspaceRootPath": "C:\\Users\\bib\\Desktop\\version1\\LeaJobTrackerWebApp\\source\\repos\\LEA\\",
"Documents": [
{
"AbsoluteMoniker": "D:0:0:{9DE0A8E4-C990-4B5B-B91D-398EB7D34088}|LEA\\LEA.csproj|c:\\users\\bib\\desktop\\version1\\leajobtrackerwebapp\\source\\repos\\lea\\lea\\appsettings.json||{90A6B3A7-C1A3-4009-A288-E2FF89E96FA0}",
"RelativeMoniker": "D:0:0:{9DE0A8E4-C990-4B5B-B91D-398EB7D34088}|LEA\\LEA.csproj|solutionrelative:lea\\appsettings.json||{90A6B3A7-C1A3-4009-A288-E2FF89E96FA0}"
},
{
"AbsoluteMoniker": "D:0:0:{9DE0A8E4-C990-4B5B-B91D-398EB7D34088}|LEA\\LEA.csproj|c:\\users\\bib\\desktop\\version1\\leajobtrackerwebapp\\source\\repos\\lea\\lea\\data\\appdbcontext.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{9DE0A8E4-C990-4B5B-B91D-398EB7D34088}|LEA\\LEA.csproj|solutionrelative:lea\\data\\appdbcontext.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
},
{
"AbsoluteMoniker": "D:0:0:{9DE0A8E4-C990-4B5B-B91D-398EB7D34088}|LEA\\LEA.csproj|c:\\users\\bib\\desktop\\version1\\leajobtrackerwebapp\\source\\repos\\lea\\lea\\views\\applications\\index.cshtml||{40D31677-CBC0-4297-A9EF-89D907823A98}",
"RelativeMoniker": "D:0:0:{9DE0A8E4-C990-4B5B-B91D-398EB7D34088}|LEA\\LEA.csproj|solutionrelative:lea\\views\\applications\\index.cshtml||{40D31677-CBC0-4297-A9EF-89D907823A98}"
},
{
"AbsoluteMoniker": "D:0:0:{9DE0A8E4-C990-4B5B-B91D-398EB7D34088}|LEA\\LEA.csproj|c:\\users\\bib\\desktop\\version1\\leajobtrackerwebapp\\source\\repos\\lea\\lea\\views\\home\\index.cshtml||{40D31677-CBC0-4297-A9EF-89D907823A98}",
"RelativeMoniker": "D:0:0:{9DE0A8E4-C990-4B5B-B91D-398EB7D34088}|LEA\\LEA.csproj|solutionrelative:lea\\views\\home\\index.cshtml||{40D31677-CBC0-4297-A9EF-89D907823A98}"
},
{
"AbsoluteMoniker": "D:0:0:{9DE0A8E4-C990-4B5B-B91D-398EB7D34088}|LEA\\LEA.csproj|c:\\users\\bib\\desktop\\version1\\leajobtrackerwebapp\\source\\repos\\lea\\lea\\views\\shared\\_layout.cshtml||{40D31677-CBC0-4297-A9EF-89D907823A98}",
"RelativeMoniker": "D:0:0:{9DE0A8E4-C990-4B5B-B91D-398EB7D34088}|LEA\\LEA.csproj|solutionrelative:lea\\views\\shared\\_layout.cshtml||{40D31677-CBC0-4297-A9EF-89D907823A98}"
},
{
"AbsoluteMoniker": "D:0:0:{9DE0A8E4-C990-4B5B-B91D-398EB7D34088}|LEA\\LEA.csproj|c:\\users\\bib\\desktop\\version1\\leajobtrackerwebapp\\source\\repos\\lea\\lea\\controllers\\homecontroller.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{9DE0A8E4-C990-4B5B-B91D-398EB7D34088}|LEA\\LEA.csproj|solutionrelative:lea\\controllers\\homecontroller.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
},
{
"AbsoluteMoniker": "D:0:0:{9DE0A8E4-C990-4B5B-B91D-398EB7D34088}|LEA\\LEA.csproj|c:\\users\\bib\\desktop\\version1\\leajobtrackerwebapp\\source\\repos\\lea\\lea\\migrations\\appdbcontextmodelsnapshot.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{9DE0A8E4-C990-4B5B-B91D-398EB7D34088}|LEA\\LEA.csproj|solutionrelative:lea\\migrations\\appdbcontextmodelsnapshot.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
},
{
"AbsoluteMoniker": "D:0:0:{9DE0A8E4-C990-4B5B-B91D-398EB7D34088}|LEA\\LEA.csproj|c:\\users\\bib\\desktop\\version1\\leajobtrackerwebapp\\source\\repos\\lea\\lea\\models\\errorviewmodel.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{9DE0A8E4-C990-4B5B-B91D-398EB7D34088}|LEA\\LEA.csproj|solutionrelative:lea\\models\\errorviewmodel.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
},
{
"AbsoluteMoniker": "D:0:0:{9DE0A8E4-C990-4B5B-B91D-398EB7D34088}|LEA\\LEA.csproj|c:\\users\\bib\\desktop\\version1\\leajobtrackerwebapp\\source\\repos\\lea\\lea\\models\\application.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{9DE0A8E4-C990-4B5B-B91D-398EB7D34088}|LEA\\LEA.csproj|solutionrelative:lea\\models\\application.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
},
{
"AbsoluteMoniker": "D:0:0:{9DE0A8E4-C990-4B5B-B91D-398EB7D34088}|LEA\\LEA.csproj|c:\\users\\bib\\desktop\\version1\\leajobtrackerwebapp\\source\\repos\\lea\\lea\\program.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{9DE0A8E4-C990-4B5B-B91D-398EB7D34088}|LEA\\LEA.csproj|solutionrelative:lea\\program.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
},
{
"AbsoluteMoniker": "D:0:0:{9DE0A8E4-C990-4B5B-B91D-398EB7D34088}|LEA\\LEA.csproj|c:\\users\\bib\\desktop\\version1\\leajobtrackerwebapp\\source\\repos\\lea\\lea\\lea.csproj||{FA3CD31E-987B-443A-9B81-186104E8DAC1}|",
"RelativeMoniker": "D:0:0:{9DE0A8E4-C990-4B5B-B91D-398EB7D34088}|LEA\\LEA.csproj|solutionrelative:lea\\lea.csproj||{FA3CD31E-987B-443A-9B81-186104E8DAC1}|"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\bib\\source\\repos\\LEA\\LEA\\Migrations\\20250915125419_InitialCreate.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
}
],
"DocumentGroupContainers": [
{
"Orientation": 0,
"VerticalTabListWidth": 256,
"DocumentGroups": [
{
"DockedWidth": 247,
"SelectedChildIndex": 6,
"Children": [
{
"$type": "Document",
"DocumentIndex": 2,
"Title": "Index.cshtml",
"DocumentMoniker": "C:\\Users\\bib\\Desktop\\version1\\LeaJobTrackerWebApp\\source\\repos\\LEA\\LEA\\Views\\Applications\\Index.cshtml",
"RelativeDocumentMoniker": "LEA\\Views\\Applications\\Index.cshtml",
"ToolTip": "C:\\Users\\bib\\Desktop\\version1\\LeaJobTrackerWebApp\\source\\repos\\LEA\\LEA\\Views\\Applications\\Index.cshtml",
"RelativeToolTip": "LEA\\Views\\Applications\\Index.cshtml",
"ViewState": "AgIAAAMAAAAAAAAAAAAAABAAAAAoAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000759|",
"WhenOpened": "2025-09-16T19:20:51.686Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 4,
"Title": "_Layout.cshtml",
"DocumentMoniker": "C:\\Users\\bib\\Desktop\\version1\\LeaJobTrackerWebApp\\source\\repos\\LEA\\LEA\\Views\\Shared\\_Layout.cshtml",
"RelativeDocumentMoniker": "LEA\\Views\\Shared\\_Layout.cshtml",
"ToolTip": "C:\\Users\\bib\\Desktop\\version1\\LeaJobTrackerWebApp\\source\\repos\\LEA\\LEA\\Views\\Shared\\_Layout.cshtml",
"RelativeToolTip": "LEA\\Views\\Shared\\_Layout.cshtml",
"ViewState": "AgIAAB0AAAAAAAAAAAA2wDEAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000759|",
"WhenOpened": "2025-09-16T07:24:48.625Z"
},
{
"$type": "Document",
"DocumentIndex": 6,
"Title": "AppDbContextModelSnapshot.cs",
"DocumentMoniker": "C:\\Users\\bib\\Desktop\\version1\\LeaJobTrackerWebApp\\source\\repos\\LEA\\LEA\\Migrations\\AppDbContextModelSnapshot.cs",
"RelativeDocumentMoniker": "LEA\\Migrations\\AppDbContextModelSnapshot.cs",
"ToolTip": "C:\\Users\\bib\\Desktop\\version1\\LeaJobTrackerWebApp\\source\\repos\\LEA\\LEA\\Migrations\\AppDbContextModelSnapshot.cs",
"RelativeToolTip": "LEA\\Migrations\\AppDbContextModelSnapshot.cs",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-09-16T06:55:48.849Z"
},
{
"$type": "Document",
"DocumentIndex": 11,
"Title": "20250915125419_InitialCreate.cs",
"DocumentMoniker": "C:\\Users\\bib\\source\\repos\\LEA\\LEA\\Migrations\\20250915125419_InitialCreate.cs",
"RelativeDocumentMoniker": "..\\..\\..\\..\\..\\..\\source\\repos\\LEA\\LEA\\Migrations\\20250915125419_InitialCreate.cs",
"ToolTip": "C:\\Users\\bib\\source\\repos\\LEA\\LEA\\Migrations\\20250915125419_InitialCreate.cs",
"RelativeToolTip": "..\\..\\..\\..\\..\\..\\source\\repos\\LEA\\LEA\\Migrations\\20250915125419_InitialCreate.cs",
"ViewState": "AgIAACgAAAAAAAAAAAA8wAAAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-09-15T12:54:20.465Z"
},
{
"$type": "Document",
"DocumentIndex": 1,
"Title": "AppDbContext.cs",
"DocumentMoniker": "C:\\Users\\bib\\Desktop\\version1\\LeaJobTrackerWebApp\\source\\repos\\LEA\\LEA\\Data\\AppDbContext.cs",
"RelativeDocumentMoniker": "LEA\\Data\\AppDbContext.cs",
"ToolTip": "C:\\Users\\bib\\Desktop\\version1\\LeaJobTrackerWebApp\\source\\repos\\LEA\\LEA\\Data\\AppDbContext.cs",
"RelativeToolTip": "LEA\\Data\\AppDbContext.cs",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAA4AAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-09-15T12:36:42.137Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 10,
"Title": "LEA.csproj",
"DocumentMoniker": "C:\\Users\\bib\\Desktop\\version1\\LeaJobTrackerWebApp\\source\\repos\\LEA\\LEA\\LEA.csproj",
"RelativeDocumentMoniker": "LEA\\LEA.csproj",
"ToolTip": "C:\\Users\\bib\\Desktop\\version1\\LeaJobTrackerWebApp\\source\\repos\\LEA\\LEA\\LEA.csproj",
"RelativeToolTip": "LEA\\LEA.csproj",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000758|",
"WhenOpened": "2025-09-15T12:36:14.172Z"
},
{
"$type": "Document",
"DocumentIndex": 0,
"Title": "appsettings.json",
"DocumentMoniker": "C:\\Users\\bib\\Desktop\\version1\\LeaJobTrackerWebApp\\source\\repos\\LEA\\LEA\\appsettings.json",
"RelativeDocumentMoniker": "LEA\\appsettings.json",
"ToolTip": "C:\\Users\\bib\\Desktop\\version1\\LeaJobTrackerWebApp\\source\\repos\\LEA\\LEA\\appsettings.json",
"RelativeToolTip": "LEA\\appsettings.json",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAoAAABUAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001642|",
"WhenOpened": "2025-09-15T12:30:55.972Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 3,
"Title": "Index.cshtml",
"DocumentMoniker": "C:\\Users\\bib\\Desktop\\version1\\LeaJobTrackerWebApp\\source\\repos\\LEA\\LEA\\Views\\Home\\Index.cshtml",
"RelativeDocumentMoniker": "LEA\\Views\\Home\\Index.cshtml",
"ToolTip": "C:\\Users\\bib\\Desktop\\version1\\LeaJobTrackerWebApp\\source\\repos\\LEA\\LEA\\Views\\Home\\Index.cshtml",
"RelativeToolTip": "LEA\\Views\\Home\\Index.cshtml",
"ViewState": "AgIAALkAAAAAAAAAAAA6wNIAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000759|",
"WhenOpened": "2025-09-16T19:31:48.428Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 9,
"Title": "Program.cs",
"DocumentMoniker": "C:\\Users\\bib\\Desktop\\version1\\LeaJobTrackerWebApp\\source\\repos\\LEA\\LEA\\Program.cs",
"RelativeDocumentMoniker": "LEA\\Program.cs",
"ToolTip": "C:\\Users\\bib\\Desktop\\version1\\LeaJobTrackerWebApp\\source\\repos\\LEA\\LEA\\Program.cs",
"RelativeToolTip": "LEA\\Program.cs",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAkAAAA9AAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-09-15T12:29:41.456Z"
},
{
"$type": "Document",
"DocumentIndex": 8,
"Title": "Application.cs",
"DocumentMoniker": "C:\\Users\\bib\\Desktop\\version1\\LeaJobTrackerWebApp\\source\\repos\\LEA\\LEA\\Models\\Application.cs",
"RelativeDocumentMoniker": "LEA\\Models\\Application.cs",
"ToolTip": "C:\\Users\\bib\\Desktop\\version1\\LeaJobTrackerWebApp\\source\\repos\\LEA\\LEA\\Models\\Application.cs",
"RelativeToolTip": "LEA\\Models\\Application.cs",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAsAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-09-15T12:26:34.472Z"
},
{
"$type": "Document",
"DocumentIndex": 5,
"Title": "HomeController.cs",
"DocumentMoniker": "C:\\Users\\bib\\Desktop\\version1\\LeaJobTrackerWebApp\\source\\repos\\LEA\\LEA\\Controllers\\HomeController.cs",
"RelativeDocumentMoniker": "LEA\\Controllers\\HomeController.cs",
"ToolTip": "C:\\Users\\bib\\Desktop\\version1\\LeaJobTrackerWebApp\\source\\repos\\LEA\\LEA\\Controllers\\HomeController.cs",
"RelativeToolTip": "LEA\\Controllers\\HomeController.cs",
"ViewState": "AgIAABAAAAAAAAAAAAAqwAAAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-09-15T12:24:01.054Z"
},
{
"$type": "Document",
"DocumentIndex": 7,
"Title": "ErrorViewModel.cs",
"DocumentMoniker": "C:\\Users\\bib\\Desktop\\version1\\LeaJobTrackerWebApp\\source\\repos\\LEA\\LEA\\Models\\ErrorViewModel.cs",
"RelativeDocumentMoniker": "LEA\\Models\\ErrorViewModel.cs",
"ToolTip": "C:\\Users\\bib\\Desktop\\version1\\LeaJobTrackerWebApp\\source\\repos\\LEA\\LEA\\Models\\ErrorViewModel.cs",
"RelativeToolTip": "LEA\\Models\\ErrorViewModel.cs",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-09-15T12:25:42.788Z"
}
]
}
]
}
]
}

View File

@@ -0,0 +1,215 @@
{
"Version": 1,
"WorkspaceRootPath": "C:\\Users\\bib\\Desktop\\version1\\LeaJobTrackerWebApp\\source\\repos\\LEA\\",
"Documents": [
{
"AbsoluteMoniker": "D:0:0:{9DE0A8E4-C990-4B5B-B91D-398EB7D34088}|LEA\\LEA.csproj|c:\\users\\bib\\desktop\\version1\\leajobtrackerwebapp\\source\\repos\\lea\\lea\\data\\appdbcontext.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{9DE0A8E4-C990-4B5B-B91D-398EB7D34088}|LEA\\LEA.csproj|solutionrelative:lea\\data\\appdbcontext.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
},
{
"AbsoluteMoniker": "D:0:0:{9DE0A8E4-C990-4B5B-B91D-398EB7D34088}|LEA\\LEA.csproj|c:\\users\\bib\\desktop\\version1\\leajobtrackerwebapp\\source\\repos\\lea\\lea\\appsettings.json||{90A6B3A7-C1A3-4009-A288-E2FF89E96FA0}",
"RelativeMoniker": "D:0:0:{9DE0A8E4-C990-4B5B-B91D-398EB7D34088}|LEA\\LEA.csproj|solutionrelative:lea\\appsettings.json||{90A6B3A7-C1A3-4009-A288-E2FF89E96FA0}"
},
{
"AbsoluteMoniker": "D:0:0:{9DE0A8E4-C990-4B5B-B91D-398EB7D34088}|LEA\\LEA.csproj|c:\\users\\bib\\desktop\\version1\\leajobtrackerwebapp\\source\\repos\\lea\\lea\\views\\applications\\index.cshtml||{40D31677-CBC0-4297-A9EF-89D907823A98}",
"RelativeMoniker": "D:0:0:{9DE0A8E4-C990-4B5B-B91D-398EB7D34088}|LEA\\LEA.csproj|solutionrelative:lea\\views\\applications\\index.cshtml||{40D31677-CBC0-4297-A9EF-89D907823A98}"
},
{
"AbsoluteMoniker": "D:0:0:{9DE0A8E4-C990-4B5B-B91D-398EB7D34088}|LEA\\LEA.csproj|c:\\users\\bib\\desktop\\version1\\leajobtrackerwebapp\\source\\repos\\lea\\lea\\views\\home\\index.cshtml||{40D31677-CBC0-4297-A9EF-89D907823A98}",
"RelativeMoniker": "D:0:0:{9DE0A8E4-C990-4B5B-B91D-398EB7D34088}|LEA\\LEA.csproj|solutionrelative:lea\\views\\home\\index.cshtml||{40D31677-CBC0-4297-A9EF-89D907823A98}"
},
{
"AbsoluteMoniker": "D:0:0:{9DE0A8E4-C990-4B5B-B91D-398EB7D34088}|LEA\\LEA.csproj|c:\\users\\bib\\desktop\\version1\\leajobtrackerwebapp\\source\\repos\\lea\\lea\\views\\shared\\_layout.cshtml||{40D31677-CBC0-4297-A9EF-89D907823A98}",
"RelativeMoniker": "D:0:0:{9DE0A8E4-C990-4B5B-B91D-398EB7D34088}|LEA\\LEA.csproj|solutionrelative:lea\\views\\shared\\_layout.cshtml||{40D31677-CBC0-4297-A9EF-89D907823A98}"
},
{
"AbsoluteMoniker": "D:0:0:{9DE0A8E4-C990-4B5B-B91D-398EB7D34088}|LEA\\LEA.csproj|c:\\users\\bib\\desktop\\version1\\leajobtrackerwebapp\\source\\repos\\lea\\lea\\controllers\\homecontroller.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{9DE0A8E4-C990-4B5B-B91D-398EB7D34088}|LEA\\LEA.csproj|solutionrelative:lea\\controllers\\homecontroller.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
},
{
"AbsoluteMoniker": "D:0:0:{9DE0A8E4-C990-4B5B-B91D-398EB7D34088}|LEA\\LEA.csproj|c:\\users\\bib\\desktop\\version1\\leajobtrackerwebapp\\source\\repos\\lea\\lea\\migrations\\appdbcontextmodelsnapshot.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{9DE0A8E4-C990-4B5B-B91D-398EB7D34088}|LEA\\LEA.csproj|solutionrelative:lea\\migrations\\appdbcontextmodelsnapshot.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
},
{
"AbsoluteMoniker": "D:0:0:{9DE0A8E4-C990-4B5B-B91D-398EB7D34088}|LEA\\LEA.csproj|c:\\users\\bib\\desktop\\version1\\leajobtrackerwebapp\\source\\repos\\lea\\lea\\models\\errorviewmodel.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{9DE0A8E4-C990-4B5B-B91D-398EB7D34088}|LEA\\LEA.csproj|solutionrelative:lea\\models\\errorviewmodel.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
},
{
"AbsoluteMoniker": "D:0:0:{9DE0A8E4-C990-4B5B-B91D-398EB7D34088}|LEA\\LEA.csproj|c:\\users\\bib\\desktop\\version1\\leajobtrackerwebapp\\source\\repos\\lea\\lea\\models\\application.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{9DE0A8E4-C990-4B5B-B91D-398EB7D34088}|LEA\\LEA.csproj|solutionrelative:lea\\models\\application.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
},
{
"AbsoluteMoniker": "D:0:0:{9DE0A8E4-C990-4B5B-B91D-398EB7D34088}|LEA\\LEA.csproj|c:\\users\\bib\\desktop\\version1\\leajobtrackerwebapp\\source\\repos\\lea\\lea\\program.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{9DE0A8E4-C990-4B5B-B91D-398EB7D34088}|LEA\\LEA.csproj|solutionrelative:lea\\program.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
},
{
"AbsoluteMoniker": "D:0:0:{9DE0A8E4-C990-4B5B-B91D-398EB7D34088}|LEA\\LEA.csproj|c:\\users\\bib\\desktop\\version1\\leajobtrackerwebapp\\source\\repos\\lea\\lea\\lea.csproj||{FA3CD31E-987B-443A-9B81-186104E8DAC1}|",
"RelativeMoniker": "D:0:0:{9DE0A8E4-C990-4B5B-B91D-398EB7D34088}|LEA\\LEA.csproj|solutionrelative:lea\\lea.csproj||{FA3CD31E-987B-443A-9B81-186104E8DAC1}|"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\bib\\source\\repos\\LEA\\LEA\\Migrations\\20250915125419_InitialCreate.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
}
],
"DocumentGroupContainers": [
{
"Orientation": 0,
"VerticalTabListWidth": 256,
"DocumentGroups": [
{
"DockedWidth": 247,
"SelectedChildIndex": 4,
"Children": [
{
"$type": "Document",
"DocumentIndex": 2,
"Title": "Index.cshtml",
"DocumentMoniker": "C:\\Users\\bib\\Desktop\\version1\\LeaJobTrackerWebApp\\source\\repos\\LEA\\LEA\\Views\\Applications\\Index.cshtml",
"RelativeDocumentMoniker": "LEA\\Views\\Applications\\Index.cshtml",
"ToolTip": "C:\\Users\\bib\\Desktop\\version1\\LeaJobTrackerWebApp\\source\\repos\\LEA\\LEA\\Views\\Applications\\Index.cshtml",
"RelativeToolTip": "LEA\\Views\\Applications\\Index.cshtml",
"ViewState": "AgIAAAMAAAAAAAAAAAAAABAAAAAoAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000759|",
"WhenOpened": "2025-09-16T19:20:51.686Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 4,
"Title": "_Layout.cshtml",
"DocumentMoniker": "C:\\Users\\bib\\Desktop\\version1\\LeaJobTrackerWebApp\\source\\repos\\LEA\\LEA\\Views\\Shared\\_Layout.cshtml",
"RelativeDocumentMoniker": "LEA\\Views\\Shared\\_Layout.cshtml",
"ToolTip": "C:\\Users\\bib\\Desktop\\version1\\LeaJobTrackerWebApp\\source\\repos\\LEA\\LEA\\Views\\Shared\\_Layout.cshtml",
"RelativeToolTip": "LEA\\Views\\Shared\\_Layout.cshtml",
"ViewState": "AgIAAB0AAAAAAAAAAAA2wDEAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000759|",
"WhenOpened": "2025-09-16T07:24:48.625Z"
},
{
"$type": "Document",
"DocumentIndex": 6,
"Title": "AppDbContextModelSnapshot.cs",
"DocumentMoniker": "C:\\Users\\bib\\Desktop\\version1\\LeaJobTrackerWebApp\\source\\repos\\LEA\\LEA\\Migrations\\AppDbContextModelSnapshot.cs",
"RelativeDocumentMoniker": "LEA\\Migrations\\AppDbContextModelSnapshot.cs",
"ToolTip": "C:\\Users\\bib\\Desktop\\version1\\LeaJobTrackerWebApp\\source\\repos\\LEA\\LEA\\Migrations\\AppDbContextModelSnapshot.cs",
"RelativeToolTip": "LEA\\Migrations\\AppDbContextModelSnapshot.cs",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-09-16T06:55:48.849Z"
},
{
"$type": "Document",
"DocumentIndex": 11,
"Title": "20250915125419_InitialCreate.cs",
"DocumentMoniker": "C:\\Users\\bib\\source\\repos\\LEA\\LEA\\Migrations\\20250915125419_InitialCreate.cs",
"RelativeDocumentMoniker": "..\\..\\..\\..\\..\\..\\source\\repos\\LEA\\LEA\\Migrations\\20250915125419_InitialCreate.cs",
"ToolTip": "C:\\Users\\bib\\source\\repos\\LEA\\LEA\\Migrations\\20250915125419_InitialCreate.cs",
"RelativeToolTip": "..\\..\\..\\..\\..\\..\\source\\repos\\LEA\\LEA\\Migrations\\20250915125419_InitialCreate.cs",
"ViewState": "AgIAACgAAAAAAAAAAAA8wAAAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-09-15T12:54:20.465Z"
},
{
"$type": "Document",
"DocumentIndex": 0,
"Title": "AppDbContext.cs",
"DocumentMoniker": "C:\\Users\\bib\\Desktop\\version1\\LeaJobTrackerWebApp\\source\\repos\\LEA\\LEA\\Data\\AppDbContext.cs",
"RelativeDocumentMoniker": "LEA\\Data\\AppDbContext.cs",
"ToolTip": "C:\\Users\\bib\\Desktop\\version1\\LeaJobTrackerWebApp\\source\\repos\\LEA\\LEA\\Data\\AppDbContext.cs",
"RelativeToolTip": "LEA\\Data\\AppDbContext.cs",
"ViewState": "AgIAAAAAAAAAAAAAAAAuwAgAAAALAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-09-15T12:36:42.137Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 10,
"Title": "LEA.csproj",
"DocumentMoniker": "C:\\Users\\bib\\Desktop\\version1\\LeaJobTrackerWebApp\\source\\repos\\LEA\\LEA\\LEA.csproj",
"RelativeDocumentMoniker": "LEA\\LEA.csproj",
"ToolTip": "C:\\Users\\bib\\Desktop\\version1\\LeaJobTrackerWebApp\\source\\repos\\LEA\\LEA\\LEA.csproj",
"RelativeToolTip": "LEA\\LEA.csproj",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000758|",
"WhenOpened": "2025-09-15T12:36:14.172Z"
},
{
"$type": "Document",
"DocumentIndex": 1,
"Title": "appsettings.json",
"DocumentMoniker": "C:\\Users\\bib\\Desktop\\version1\\LeaJobTrackerWebApp\\source\\repos\\LEA\\LEA\\appsettings.json",
"RelativeDocumentMoniker": "LEA\\appsettings.json",
"ToolTip": "C:\\Users\\bib\\Desktop\\version1\\LeaJobTrackerWebApp\\source\\repos\\LEA\\LEA\\appsettings.json",
"RelativeToolTip": "LEA\\appsettings.json",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAoAAABUAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001642|",
"WhenOpened": "2025-09-15T12:30:55.972Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 3,
"Title": "Index.cshtml",
"DocumentMoniker": "C:\\Users\\bib\\Desktop\\version1\\LeaJobTrackerWebApp\\source\\repos\\LEA\\LEA\\Views\\Home\\Index.cshtml",
"RelativeDocumentMoniker": "LEA\\Views\\Home\\Index.cshtml",
"ToolTip": "C:\\Users\\bib\\Desktop\\version1\\LeaJobTrackerWebApp\\source\\repos\\LEA\\LEA\\Views\\Home\\Index.cshtml",
"RelativeToolTip": "LEA\\Views\\Home\\Index.cshtml",
"ViewState": "AgIAALkAAAAAAAAAAAA6wNIAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000759|",
"WhenOpened": "2025-09-16T19:31:48.428Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 9,
"Title": "Program.cs",
"DocumentMoniker": "C:\\Users\\bib\\Desktop\\version1\\LeaJobTrackerWebApp\\source\\repos\\LEA\\LEA\\Program.cs",
"RelativeDocumentMoniker": "LEA\\Program.cs",
"ToolTip": "C:\\Users\\bib\\Desktop\\version1\\LeaJobTrackerWebApp\\source\\repos\\LEA\\LEA\\Program.cs",
"RelativeToolTip": "LEA\\Program.cs",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAkAAAA9AAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-09-15T12:29:41.456Z"
},
{
"$type": "Document",
"DocumentIndex": 8,
"Title": "Application.cs",
"DocumentMoniker": "C:\\Users\\bib\\Desktop\\version1\\LeaJobTrackerWebApp\\source\\repos\\LEA\\LEA\\Models\\Application.cs",
"RelativeDocumentMoniker": "LEA\\Models\\Application.cs",
"ToolTip": "C:\\Users\\bib\\Desktop\\version1\\LeaJobTrackerWebApp\\source\\repos\\LEA\\LEA\\Models\\Application.cs",
"RelativeToolTip": "LEA\\Models\\Application.cs",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAsAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-09-15T12:26:34.472Z"
},
{
"$type": "Document",
"DocumentIndex": 5,
"Title": "HomeController.cs",
"DocumentMoniker": "C:\\Users\\bib\\Desktop\\version1\\LeaJobTrackerWebApp\\source\\repos\\LEA\\LEA\\Controllers\\HomeController.cs",
"RelativeDocumentMoniker": "LEA\\Controllers\\HomeController.cs",
"ToolTip": "C:\\Users\\bib\\Desktop\\version1\\LeaJobTrackerWebApp\\source\\repos\\LEA\\LEA\\Controllers\\HomeController.cs",
"RelativeToolTip": "LEA\\Controllers\\HomeController.cs",
"ViewState": "AgIAABAAAAAAAAAAAAAqwAAAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-09-15T12:24:01.054Z"
},
{
"$type": "Document",
"DocumentIndex": 7,
"Title": "ErrorViewModel.cs",
"DocumentMoniker": "C:\\Users\\bib\\Desktop\\version1\\LeaJobTrackerWebApp\\source\\repos\\LEA\\LEA\\Models\\ErrorViewModel.cs",
"RelativeDocumentMoniker": "LEA\\Models\\ErrorViewModel.cs",
"ToolTip": "C:\\Users\\bib\\Desktop\\version1\\LeaJobTrackerWebApp\\source\\repos\\LEA\\LEA\\Models\\ErrorViewModel.cs",
"RelativeToolTip": "LEA\\Models\\ErrorViewModel.cs",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-09-15T12:25:42.788Z"
}
]
}
]
}
]
}

Binary file not shown.

22
Desktop/hallo/LEA.sln Normal file
View File

@@ -0,0 +1,22 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.12.35728.132 d17.12
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LEA", "LEA\LEA.csproj", "{9DE0A8E4-C990-4B5B-B91D-398EB7D34088}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{9DE0A8E4-C990-4B5B-B91D-398EB7D34088}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9DE0A8E4-C990-4B5B-B91D-398EB7D34088}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9DE0A8E4-C990-4B5B-B91D-398EB7D34088}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9DE0A8E4-C990-4B5B-B91D-398EB7D34088}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

View File

@@ -0,0 +1,138 @@
using System.Security.Claims;
using LEA.Models;
using LEA.ViewModels;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
namespace LEA.Controllers;
public class AccountController : Controller
{
private readonly UserManager<ApplicationUser> _userManager;
private readonly SignInManager<ApplicationUser> _signInManager;
private readonly ILogger<AccountController> _logger;
public AccountController(
UserManager<ApplicationUser> userManager,
SignInManager<ApplicationUser> signInManager,
ILogger<AccountController> logger)
{
_userManager = userManager;
_signInManager = signInManager;
_logger = logger;
}
[HttpGet]
[AllowAnonymous]
public IActionResult Register()
{
return View(new RegisterViewModel());
}
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Register(RegisterViewModel model)
{
if (!ModelState.IsValid)
{
return View(model);
}
var existingUser = await _userManager.FindByEmailAsync(model.Email);
if (existingUser != null)
{
ModelState.AddModelError(nameof(model.Email), "Diese E-Mail-Adresse wird bereits verwendet.");
return View(model);
}
var user = new ApplicationUser
{
FullName = model.FullName.Trim(),
Email = model.Email.Trim(),
UserName = model.Email.Trim(),
CreatedAt = DateTime.UtcNow
};
var result = await _userManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
await _userManager.AddClaimAsync(user, new Claim("FullName", user.FullName));
await _signInManager.SignInAsync(user, isPersistent: true);
_logger.LogInformation("Neuer Benutzer {Email} wurde erstellt und angemeldet.", user.Email);
TempData["Success"] = "Registrierung erfolgreich. Willkommen zurück!";
return RedirectToAction("Index", "Applications");
}
foreach (var error in result.Errors)
{
ModelState.AddModelError(string.Empty, TranslateIdentityError(error));
}
return View(model);
}
[HttpGet]
[AllowAnonymous]
public IActionResult Login(string? returnUrl = null)
{
return View(new LoginViewModel { ReturnUrl = returnUrl });
}
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Login(LoginViewModel model)
{
if (!ModelState.IsValid)
{
return View(model);
}
var user = await _userManager.FindByEmailAsync(model.Email);
if (user == null)
{
ModelState.AddModelError(string.Empty, "Ungültige Anmeldedaten.");
return View(model);
}
var result = await _signInManager.PasswordSignInAsync(user, model.Password, model.RememberMe, lockoutOnFailure: false);
if (result.Succeeded)
{
_logger.LogInformation("Benutzer {Email} hat sich angemeldet.", user.Email);
if (!string.IsNullOrWhiteSpace(model.ReturnUrl) && Url.IsLocalUrl(model.ReturnUrl))
{
return Redirect(model.ReturnUrl);
}
TempData["Success"] = "Erfolgreich angemeldet.";
return RedirectToAction("Index", "Applications");
}
ModelState.AddModelError(string.Empty, "Ungültige Anmeldedaten.");
return View(model);
}
[HttpPost]
[Authorize]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Logout()
{
await _signInManager.SignOutAsync();
TempData["Success"] = "Sie wurden abgemeldet.";
return RedirectToAction("Index", "Home");
}
private static string TranslateIdentityError(IdentityError error) => error.Code switch
{
"PasswordTooShort" => "Das Passwort ist zu kurz.",
"PasswordRequiresNonAlphanumeric" => "Das Passwort muss mindestens ein Sonderzeichen enthalten.",
"PasswordRequiresDigit" => "Das Passwort muss mindestens eine Zahl enthalten.",
"PasswordRequiresUpper" => "Das Passwort muss mindestens einen Großbuchstaben enthalten.",
"PasswordRequiresLower" => "Das Passwort muss mindestens einen Kleinbuchstaben enthalten.",
"DuplicateEmail" or "DuplicateUserName" => "Diese E-Mail-Adresse wird bereits verwendet.",
"InvalidEmail" => "Bitte eine gültige E-Mail-Adresse eingeben.",
_ => error.Description
};
}

View File

@@ -0,0 +1,289 @@
using LEA.Data;
using LEA.Extensions;
using LEA.Models;
using LEA.ViewModels;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.EntityFrameworkCore;
namespace LEA.Controllers;
[Authorize]
public class ApplicationsController : Controller
{
private readonly AppDbContext _context;
private readonly UserManager<ApplicationUser> _userManager;
public ApplicationsController(AppDbContext context, UserManager<ApplicationUser> userManager)
{
_context = context;
_userManager = userManager;
}
public async Task<IActionResult> Index(string? searchTerm, ApplicationStatus? status)
{
var userId = GetCurrentUserId();
var query = _context.Applications
.Include(a => a.Contact)
.Where(a => a.UserId == userId);
if (!string.IsNullOrWhiteSpace(searchTerm))
{
var term = $"%{searchTerm.Trim()}%";
query = query.Where(a =>
EF.Functions.Like(a.Company, term) ||
EF.Functions.Like(a.Role, term) ||
EF.Functions.Like(a.Source ?? string.Empty, term) ||
EF.Functions.Like(a.Notes ?? string.Empty, term) ||
(a.Contact != null && (
EF.Functions.Like(a.Contact.FullName ?? string.Empty, term) ||
EF.Functions.Like(a.Contact.Email ?? string.Empty, term))));
}
if (status.HasValue)
{
query = query.Where(a => a.Status == status.Value);
}
var applications = await query
.OrderByDescending(a => a.UpdatedAt)
.ThenByDescending(a => a.AppliedOn)
.ToListAsync();
var statsQuery = _context.Applications.Where(a => a.UserId == userId);
var statusCounts = await statsQuery
.GroupBy(a => a.Status)
.Select(g => new { g.Key, Count = g.Count() })
.ToListAsync();
var statusDictionary = Enum.GetValues<ApplicationStatus>()
.ToDictionary(s => s, s => statusCounts.FirstOrDefault(sc => sc.Key == s)?.Count ?? 0);
var model = new ApplicationListViewModel
{
Applications = applications,
SearchTerm = searchTerm,
StatusFilter = status,
StatusOptions = GetStatusSelectList(status, includeAllOption: true),
StatusCounts = statusDictionary,
TotalApplications = await statsQuery.CountAsync()
};
return View(model);
}
[HttpGet]
public IActionResult Create()
{
var model = new ApplicationFormViewModel
{
AppliedOn = DateTime.Today,
Status = ApplicationStatus.Applied,
StatusOptions = GetStatusSelectList(ApplicationStatus.Applied),
Heading = "Bewerbung hinzufügen",
SubmitText = "Speichern"
};
return View(model);
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create(ApplicationFormViewModel model)
{
if (!ModelState.IsValid)
{
model.StatusOptions = GetStatusSelectList(model.Status);
model.Heading = "Bewerbung hinzufügen";
model.SubmitText = "Speichern";
return View(model);
}
var application = new Application
{
Role = model.Role.Trim(),
Company = model.Company.Trim(),
Source = string.IsNullOrWhiteSpace(model.Source) ? null : model.Source.Trim(),
Status = model.Status,
AppliedOn = model.AppliedOn,
Notes = string.IsNullOrWhiteSpace(model.Notes) ? null : model.Notes.Trim(),
UserId = GetCurrentUserId(),
UpdatedAt = DateTime.UtcNow
};
if (HasContactInformation(model))
{
application.Contact = new Contact
{
FullName = string.IsNullOrWhiteSpace(model.ContactName) ? null : model.ContactName.Trim(),
Email = string.IsNullOrWhiteSpace(model.ContactEmail) ? null : model.ContactEmail.Trim(),
Phone = string.IsNullOrWhiteSpace(model.ContactPhone) ? null : model.ContactPhone.Trim()
};
}
_context.Applications.Add(application);
await _context.SaveChangesAsync();
TempData["Success"] = "Die Bewerbung wurde gespeichert.";
return RedirectToAction(nameof(Index));
}
[HttpGet]
public async Task<IActionResult> Edit(int id)
{
var application = await FindApplicationForCurrentUserAsync(id);
if (application == null)
{
return NotFound();
}
var model = new ApplicationFormViewModel
{
Id = application.Id,
Role = application.Role,
Company = application.Company,
Source = application.Source,
Status = application.Status,
AppliedOn = application.AppliedOn,
Notes = application.Notes,
ContactName = application.Contact?.FullName,
ContactEmail = application.Contact?.Email,
ContactPhone = application.Contact?.Phone,
StatusOptions = GetStatusSelectList(application.Status),
Heading = "Bewerbung bearbeiten",
SubmitText = "Änderungen speichern"
};
return View(model);
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(int id, ApplicationFormViewModel model)
{
if (id != model.Id)
{
return NotFound();
}
if (!ModelState.IsValid)
{
model.StatusOptions = GetStatusSelectList(model.Status);
model.Heading = "Bewerbung bearbeiten";
model.SubmitText = "Änderungen speichern";
return View(model);
}
var application = await FindApplicationForCurrentUserAsync(id);
if (application == null)
{
return NotFound();
}
application.Role = model.Role.Trim();
application.Company = model.Company.Trim();
application.Source = string.IsNullOrWhiteSpace(model.Source) ? null : model.Source.Trim();
application.Status = model.Status;
application.AppliedOn = model.AppliedOn;
application.Notes = string.IsNullOrWhiteSpace(model.Notes) ? null : model.Notes.Trim();
application.UpdatedAt = DateTime.UtcNow;
if (HasContactInformation(model))
{
if (application.Contact == null)
{
application.Contact = new Contact();
}
application.Contact.FullName = string.IsNullOrWhiteSpace(model.ContactName) ? null : model.ContactName.Trim();
application.Contact.Email = string.IsNullOrWhiteSpace(model.ContactEmail) ? null : model.ContactEmail.Trim();
application.Contact.Phone = string.IsNullOrWhiteSpace(model.ContactPhone) ? null : model.ContactPhone.Trim();
}
else if (application.Contact != null)
{
_context.Contacts.Remove(application.Contact);
}
await _context.SaveChangesAsync();
TempData["Success"] = "Die Bewerbung wurde aktualisiert.";
return RedirectToAction(nameof(Index));
}
[HttpGet]
public async Task<IActionResult> Details(int id)
{
var application = await FindApplicationForCurrentUserAsync(id);
if (application == null)
{
return NotFound();
}
return View(application);
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Delete(int id)
{
var application = await FindApplicationForCurrentUserAsync(id);
if (application == null)
{
return NotFound();
}
_context.Applications.Remove(application);
await _context.SaveChangesAsync();
TempData["Success"] = "Die Bewerbung wurde gelöscht.";
return RedirectToAction(nameof(Index));
}
private async Task<Application?> FindApplicationForCurrentUserAsync(int id)
{
var userId = GetCurrentUserId();
return await _context.Applications
.Include(a => a.Contact)
.Where(a => a.UserId == userId)
.FirstOrDefaultAsync(a => a.Id == id);
}
private IEnumerable<SelectListItem> GetStatusSelectList(ApplicationStatus? selectedStatus, bool includeAllOption = false)
{
var options = Enum.GetValues<ApplicationStatus>()
.Select(status => new SelectListItem
{
Text = status.GetDisplayName(),
Value = status.ToString(),
Selected = selectedStatus.HasValue && selectedStatus.Value == status
})
.ToList();
if (includeAllOption)
{
options.Insert(0, new SelectListItem
{
Text = "Alle Status",
Value = string.Empty,
Selected = !selectedStatus.HasValue
});
}
return options;
}
private bool HasContactInformation(ApplicationFormViewModel model)
{
return !string.IsNullOrWhiteSpace(model.ContactName)
|| !string.IsNullOrWhiteSpace(model.ContactEmail)
|| !string.IsNullOrWhiteSpace(model.ContactPhone);
}
private string GetCurrentUserId()
{
return _userManager.GetUserId(User)
?? throw new InvalidOperationException("Benutzer ist nicht angemeldet.");
}
}

View File

@@ -0,0 +1,97 @@
using System.Diagnostics;
using System.Globalization;
using LEA.Data;
using LEA.Models;
using LEA.ViewModels;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
namespace LEA.Controllers;
public class HomeController : Controller
{
private readonly ILogger<HomeController> _logger;
private readonly AppDbContext _context;
private readonly UserManager<ApplicationUser> _userManager;
public HomeController(
ILogger<HomeController> logger,
AppDbContext context,
UserManager<ApplicationUser> userManager)
{
_logger = logger;
_context = context;
_userManager = userManager;
}
public async Task<IActionResult> Index()
{
var viewModel = new DashboardViewModel();
if (User.Identity?.IsAuthenticated ?? false)
{
var userId = _userManager.GetUserId(User);
var user = await _userManager.GetUserAsync(User);
var applications = await _context.Applications
.Include(a => a.Contact)
.Where(a => a.UserId == userId)
.OrderByDescending(a => a.UpdatedAt)
.ThenByDescending(a => a.AppliedOn)
.Take(5)
.ToListAsync();
var statusCounts = await _context.Applications
.Where(a => a.UserId == userId)
.GroupBy(a => a.Status)
.Select(group => new { group.Key, Count = group.Count() })
.ToListAsync();
var startOfCurrentMonth = new DateTime(DateTime.Today.Year, DateTime.Today.Month, 1);
var startDate = startOfCurrentMonth.AddMonths(-5);
var monthlyResults = await _context.Applications
.Where(a => a.UserId == userId && a.AppliedOn >= startDate)
.GroupBy(a => new { a.AppliedOn.Year, a.AppliedOn.Month })
.Select(group => new { group.Key.Year, group.Key.Month, Count = group.Count() })
.ToListAsync();
var germanCulture = CultureInfo.GetCultureInfo("de-DE");
var monthlyStatistics = new List<MonthlyApplicationStat>();
for (var offset = 0; offset < 6; offset++)
{
var currentMonth = startDate.AddMonths(offset);
var match = monthlyResults.FirstOrDefault(result =>
result.Year == currentMonth.Year && result.Month == currentMonth.Month);
var label = currentMonth.ToString("MMM yyyy", germanCulture);
monthlyStatistics.Add(new MonthlyApplicationStat(label, match?.Count ?? 0));
}
viewModel.IsAuthenticated = true;
viewModel.FullName = user?.FullName;
viewModel.RecentApplications = applications;
viewModel.TotalApplications = statusCounts.Sum(sc => sc.Count);
viewModel.AppliedCount = statusCounts.FirstOrDefault(sc => sc.Key == ApplicationStatus.Applied)?.Count ?? 0;
viewModel.InterviewCount = statusCounts.FirstOrDefault(sc => sc.Key == ApplicationStatus.Interview)?.Count ?? 0;
viewModel.OfferCount = statusCounts.FirstOrDefault(sc => sc.Key == ApplicationStatus.Offer)?.Count ?? 0;
viewModel.RejectedCount = statusCounts.FirstOrDefault(sc => sc.Key == ApplicationStatus.Rejected)?.Count ?? 0;
viewModel.MonthlyApplications = monthlyStatistics;
}
return View(viewModel);
}
public IActionResult Privacy()
{
return View();
}
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public IActionResult Error()
{
return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
}
}

View File

@@ -0,0 +1,37 @@
using LEA.Models;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
namespace LEA.Data;
public class AppDbContext : IdentityDbContext<ApplicationUser>
{
public AppDbContext(DbContextOptions<AppDbContext> options) : base(options)
{
}
public DbSet<Application> Applications { get; set; } = null!;
public DbSet<Contact> Contacts { get; set; } = null!;
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<Application>()
.HasOne(a => a.User)
.WithMany()
.HasForeignKey(a => a.UserId)
.OnDelete(DeleteBehavior.Cascade);
builder.Entity<Application>()
.HasOne(a => a.Contact)
.WithOne(c => c.Application)
.HasForeignKey<Contact>(c => c.ApplicationId)
.OnDelete(DeleteBehavior.Cascade);
builder.Entity<Application>()
.Property(a => a.Status)
.HasConversion<string>()
.HasMaxLength(50);
}
}

View File

@@ -0,0 +1,15 @@
using System;
using System.ComponentModel.DataAnnotations;
using System.Reflection;
namespace LEA.Extensions;
public static class EnumExtensions
{
public static string GetDisplayName(this Enum value)
{
var memberInfo = value.GetType().GetMember(value.ToString()).FirstOrDefault();
var displayAttribute = memberInfo?.GetCustomAttribute<DisplayAttribute>();
return displayAttribute?.GetName() ?? value.ToString();
}
}

View File

@@ -0,0 +1,23 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="8.0.8" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.8">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.8">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="MySqlConnector" Version="2.3.7" />
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="8.0.2" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<ActiveDebugProfile>https</ActiveDebugProfile>
</PropertyGroup>
</Project>

View File

@@ -0,0 +1,343 @@
using System;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Pomelo.EntityFrameworkCore.MySql.Metadata;
#nullable disable
namespace LEA.Migrations
{
/// <inheritdoc />
public partial class InitialCreate : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AlterDatabase()
.Annotation("MySql:CharSet", "utf8mb4")
.Annotation("Relational:MaxIdentifierLength", 64);
migrationBuilder.CreateTable(
name: "AspNetRoles",
columns: table => new
{
Id = table.Column<string>(type: "varchar(255)", nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
Name = table.Column<string>(type: "varchar(256)", maxLength: 256, nullable: true)
.Annotation("MySql:CharSet", "utf8mb4"),
NormalizedName = table.Column<string>(type: "varchar(256)", maxLength: 256, nullable: true)
.Annotation("MySql:CharSet", "utf8mb4"),
ConcurrencyStamp = table.Column<string>(type: "longtext", nullable: true)
.Annotation("MySql:CharSet", "utf8mb4")
},
constraints: table =>
{
table.PrimaryKey("PK_AspNetRoles", x => x.Id);
})
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.CreateTable(
name: "AspNetUsers",
columns: table => new
{
Id = table.Column<string>(type: "varchar(255)", nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
FullName = table.Column<string>(type: "varchar(100)", maxLength: 100, nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
CreatedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false),
UserName = table.Column<string>(type: "varchar(256)", maxLength: 256, nullable: true)
.Annotation("MySql:CharSet", "utf8mb4"),
NormalizedUserName = table.Column<string>(type: "varchar(256)", maxLength: 256, nullable: true)
.Annotation("MySql:CharSet", "utf8mb4"),
Email = table.Column<string>(type: "varchar(256)", maxLength: 256, nullable: true)
.Annotation("MySql:CharSet", "utf8mb4"),
NormalizedEmail = table.Column<string>(type: "varchar(256)", maxLength: 256, nullable: true)
.Annotation("MySql:CharSet", "utf8mb4"),
EmailConfirmed = table.Column<bool>(type: "tinyint(1)", nullable: false),
PasswordHash = table.Column<string>(type: "longtext", nullable: true)
.Annotation("MySql:CharSet", "utf8mb4"),
SecurityStamp = table.Column<string>(type: "longtext", nullable: true)
.Annotation("MySql:CharSet", "utf8mb4"),
ConcurrencyStamp = table.Column<string>(type: "longtext", nullable: true)
.Annotation("MySql:CharSet", "utf8mb4"),
PhoneNumber = table.Column<string>(type: "longtext", nullable: true)
.Annotation("MySql:CharSet", "utf8mb4"),
PhoneNumberConfirmed = table.Column<bool>(type: "tinyint(1)", nullable: false),
TwoFactorEnabled = table.Column<bool>(type: "tinyint(1)", nullable: false),
LockoutEnd = table.Column<DateTimeOffset>(type: "datetime(6)", nullable: true),
LockoutEnabled = table.Column<bool>(type: "tinyint(1)", nullable: false),
AccessFailedCount = table.Column<int>(type: "int", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_AspNetUsers", x => x.Id);
})
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.CreateTable(
name: "AspNetRoleClaims",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false)
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
RoleId = table.Column<string>(type: "varchar(255)", nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
ClaimType = table.Column<string>(type: "longtext", nullable: true)
.Annotation("MySql:CharSet", "utf8mb4"),
ClaimValue = table.Column<string>(type: "longtext", nullable: true)
.Annotation("MySql:CharSet", "utf8mb4")
},
constraints: table =>
{
table.PrimaryKey("PK_AspNetRoleClaims", x => x.Id);
table.ForeignKey(
name: "FK_AspNetRoleClaims_AspNetRoles_RoleId",
column: x => x.RoleId,
principalTable: "AspNetRoles",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
})
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.CreateTable(
name: "AspNetUserClaims",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false)
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
UserId = table.Column<string>(type: "varchar(255)", nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
ClaimType = table.Column<string>(type: "longtext", nullable: true)
.Annotation("MySql:CharSet", "utf8mb4"),
ClaimValue = table.Column<string>(type: "longtext", nullable: true)
.Annotation("MySql:CharSet", "utf8mb4")
},
constraints: table =>
{
table.PrimaryKey("PK_AspNetUserClaims", x => x.Id);
table.ForeignKey(
name: "FK_AspNetUserClaims_AspNetUsers_UserId",
column: x => x.UserId,
principalTable: "AspNetUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
})
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.CreateTable(
name: "AspNetUserLogins",
columns: table => new
{
LoginProvider = table.Column<string>(type: "varchar(128)", maxLength: 128, nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
ProviderKey = table.Column<string>(type: "varchar(128)", maxLength: 128, nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
ProviderDisplayName = table.Column<string>(type: "longtext", nullable: true)
.Annotation("MySql:CharSet", "utf8mb4"),
UserId = table.Column<string>(type: "varchar(255)", nullable: false)
.Annotation("MySql:CharSet", "utf8mb4")
},
constraints: table =>
{
table.PrimaryKey("PK_AspNetUserLogins", x => new { x.LoginProvider, x.ProviderKey });
table.ForeignKey(
name: "FK_AspNetUserLogins_AspNetUsers_UserId",
column: x => x.UserId,
principalTable: "AspNetUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
})
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.CreateTable(
name: "AspNetUserRoles",
columns: table => new
{
UserId = table.Column<string>(type: "varchar(255)", nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
RoleId = table.Column<string>(type: "varchar(255)", nullable: false)
.Annotation("MySql:CharSet", "utf8mb4")
},
constraints: table =>
{
table.PrimaryKey("PK_AspNetUserRoles", x => new { x.UserId, x.RoleId });
table.ForeignKey(
name: "FK_AspNetUserRoles_AspNetRoles_RoleId",
column: x => x.RoleId,
principalTable: "AspNetRoles",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_AspNetUserRoles_AspNetUsers_UserId",
column: x => x.UserId,
principalTable: "AspNetUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
})
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.CreateTable(
name: "AspNetUserTokens",
columns: table => new
{
UserId = table.Column<string>(type: "varchar(255)", nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
LoginProvider = table.Column<string>(type: "varchar(128)", maxLength: 128, nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
Name = table.Column<string>(type: "varchar(128)", maxLength: 128, nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
Value = table.Column<string>(type: "longtext", nullable: true)
.Annotation("MySql:CharSet", "utf8mb4")
},
constraints: table =>
{
table.PrimaryKey("PK_AspNetUserTokens", x => new { x.UserId, x.LoginProvider, x.Name });
table.ForeignKey(
name: "FK_AspNetUserTokens_AspNetUsers_UserId",
column: x => x.UserId,
principalTable: "AspNetUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
})
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.CreateTable(
name: "Applications",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false)
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
Role = table.Column<string>(type: "varchar(150)", maxLength: 150, nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
Company = table.Column<string>(type: "varchar(150)", maxLength: 150, nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
Source = table.Column<string>(type: "varchar(200)", maxLength: 200, nullable: true)
.Annotation("MySql:CharSet", "utf8mb4"),
Status = table.Column<string>(type: "varchar(50)", maxLength: 50, nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
AppliedOn = table.Column<DateTime>(type: "date", nullable: false),
Notes = table.Column<string>(type: "longtext", nullable: true)
.Annotation("MySql:CharSet", "utf8mb4"),
UpdatedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false),
UserId = table.Column<string>(type: "varchar(255)", nullable: false)
.Annotation("MySql:CharSet", "utf8mb4")
},
constraints: table =>
{
table.PrimaryKey("PK_Applications", x => x.Id);
table.ForeignKey(
name: "FK_Applications_AspNetUsers_UserId",
column: x => x.UserId,
principalTable: "AspNetUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
})
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.CreateTable(
name: "Contacts",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false)
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
FullName = table.Column<string>(type: "varchar(100)", maxLength: 100, nullable: true)
.Annotation("MySql:CharSet", "utf8mb4"),
Email = table.Column<string>(type: "longtext", nullable: true)
.Annotation("MySql:CharSet", "utf8mb4"),
Phone = table.Column<string>(type: "longtext", nullable: true)
.Annotation("MySql:CharSet", "utf8mb4"),
ApplicationId = table.Column<int>(type: "int", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Contacts", x => x.Id);
table.ForeignKey(
name: "FK_Contacts_Applications_ApplicationId",
column: x => x.ApplicationId,
principalTable: "Applications",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
})
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.CreateIndex(
name: "IX_Applications_UserId",
table: "Applications",
column: "UserId");
migrationBuilder.CreateIndex(
name: "IX_AspNetRoleClaims_RoleId",
table: "AspNetRoleClaims",
column: "RoleId");
migrationBuilder.CreateIndex(
name: "RoleNameIndex",
table: "AspNetRoles",
column: "NormalizedName",
unique: true);
migrationBuilder.CreateIndex(
name: "IX_AspNetUserClaims_UserId",
table: "AspNetUserClaims",
column: "UserId");
migrationBuilder.CreateIndex(
name: "IX_AspNetUserLogins_UserId",
table: "AspNetUserLogins",
column: "UserId");
migrationBuilder.CreateIndex(
name: "IX_AspNetUserRoles_RoleId",
table: "AspNetUserRoles",
column: "RoleId");
migrationBuilder.CreateIndex(
name: "EmailIndex",
table: "AspNetUsers",
column: "NormalizedEmail");
migrationBuilder.CreateIndex(
name: "UserNameIndex",
table: "AspNetUsers",
column: "NormalizedUserName",
unique: true);
migrationBuilder.CreateIndex(
name: "IX_Contacts_ApplicationId",
table: "Contacts",
column: "ApplicationId",
unique: true);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "AspNetRoleClaims");
migrationBuilder.DropTable(
name: "AspNetUserClaims");
migrationBuilder.DropTable(
name: "AspNetUserLogins");
migrationBuilder.DropTable(
name: "AspNetUserRoles");
migrationBuilder.DropTable(
name: "AspNetUserTokens");
migrationBuilder.DropTable(
name: "Contacts");
migrationBuilder.DropTable(
name: "AspNetRoles");
migrationBuilder.DropTable(
name: "Applications");
migrationBuilder.DropTable(
name: "AspNetUsers");
}
}
}

View File

@@ -0,0 +1,357 @@
using System;
using LEA.Data;
using LEA.Models;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Pomelo.EntityFrameworkCore.MySql.Metadata;
#nullable disable
namespace LEA.Migrations
{
[DbContext(typeof(AppDbContext))]
partial class AppDbContextModelSnapshot : ModelSnapshot
{
protected override void BuildModel(ModelBuilder modelBuilder)
{
modelBuilder
.HasAnnotation("ProductVersion", "8.0.8")
.HasAnnotation("Relational:MaxIdentifierLength", 64);
MySqlModelBuilderExtensions.HasCharSet(modelBuilder, "utf8mb4");
modelBuilder.Entity("LEA.Models.Application", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int")
.HasAnnotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn);
b.Property<DateTime>("AppliedOn")
.HasColumnType("date");
b.Property<string>("Company")
.IsRequired()
.HasMaxLength(150)
.HasColumnType("varchar(150)");
b.Property<string>("Notes")
.HasColumnType("longtext");
b.Property<string>("Role")
.IsRequired()
.HasMaxLength(150)
.HasColumnType("varchar(150)");
b.Property<string>("Source")
.HasMaxLength(200)
.HasColumnType("varchar(200)");
b.Property<ApplicationStatus>("Status")
.HasMaxLength(50)
.HasColumnType("varchar(50)")
.HasConversion<string>();
b.Property<DateTime>("UpdatedAt")
.HasColumnType("datetime(6)");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("varchar(255)");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("Applications");
b.HasOne("LEA.Models.ApplicationUser", "User")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Contact");
b.Navigation("User");
});
modelBuilder.Entity("LEA.Models.ApplicationUser", b =>
{
b.Property<string>("Id")
.HasColumnType("varchar(255)");
b.Property<int>("AccessFailedCount")
.HasColumnType("int");
b.Property<string>("ConcurrencyStamp")
.HasColumnType("longtext");
b.Property<DateTime>("CreatedAt")
.HasColumnType("datetime(6)");
b.Property<string>("Email")
.HasMaxLength(256)
.HasColumnType("varchar(256)");
b.Property<bool>("EmailConfirmed")
.HasColumnType("tinyint(1)");
b.Property<string>("FullName")
.IsRequired()
.HasMaxLength(100)
.HasColumnType("varchar(100)");
b.Property<bool>("LockoutEnabled")
.HasColumnType("tinyint(1)");
b.Property<DateTimeOffset?>("LockoutEnd")
.HasColumnType("datetime(6)");
b.Property<string>("NormalizedEmail")
.HasMaxLength(256)
.HasColumnType("varchar(256)");
b.Property<string>("NormalizedUserName")
.HasMaxLength(256)
.HasColumnType("varchar(256)");
b.Property<string>("PasswordHash")
.HasColumnType("longtext");
b.Property<string>("PhoneNumber")
.HasColumnType("longtext");
b.Property<bool>("PhoneNumberConfirmed")
.HasColumnType("tinyint(1)");
b.Property<string>("SecurityStamp")
.HasColumnType("longtext");
b.Property<bool>("TwoFactorEnabled")
.HasColumnType("tinyint(1)");
b.Property<string>("UserName")
.HasMaxLength(256)
.HasColumnType("varchar(256)");
b.HasKey("Id");
b.HasIndex("NormalizedEmail")
.HasDatabaseName("EmailIndex");
b.HasIndex("NormalizedUserName")
.IsUnique()
.HasDatabaseName("UserNameIndex");
b.ToTable("AspNetUsers", (string)null);
});
modelBuilder.Entity("LEA.Models.Contact", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int")
.HasAnnotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn);
b.Property<int>("ApplicationId")
.HasColumnType("int");
b.Property<string>("Email")
.HasColumnType("longtext");
b.Property<string>("FullName")
.HasMaxLength(100)
.HasColumnType("varchar(100)");
b.Property<string>("Phone")
.HasColumnType("longtext");
b.HasKey("Id");
b.HasIndex("ApplicationId")
.IsUnique();
b.ToTable("Contacts");
b.HasOne("LEA.Models.Application", "Application")
.WithOne("Contact")
.HasForeignKey("LEA.Models.Contact", "ApplicationId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Application");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
{
b.Property<string>("Id")
.HasColumnType("varchar(255)");
b.Property<string>("ConcurrencyStamp")
.HasColumnType("longtext");
b.Property<string>("Name")
.HasMaxLength(256)
.HasColumnType("varchar(256)");
b.Property<string>("NormalizedName")
.HasMaxLength(256)
.HasColumnType("varchar(256)");
b.HasKey("Id");
b.HasIndex("NormalizedName")
.IsUnique()
.HasDatabaseName("RoleNameIndex");
b.ToTable("AspNetRoles", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int")
.HasAnnotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn);
b.Property<string>("ClaimType")
.HasColumnType("longtext");
b.Property<string>("ClaimValue")
.HasColumnType("longtext");
b.Property<string>("RoleId")
.IsRequired()
.HasColumnType("varchar(255)");
b.HasKey("Id");
b.HasIndex("RoleId");
b.ToTable("AspNetRoleClaims", (string)null);
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int")
.HasAnnotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn);
b.Property<string>("ClaimType")
.HasColumnType("longtext");
b.Property<string>("ClaimValue")
.HasColumnType("longtext");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("varchar(255)");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("AspNetUserClaims", (string)null);
b.HasOne("LEA.Models.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.Property<string>("LoginProvider")
.HasMaxLength(128)
.HasColumnType("varchar(128)");
b.Property<string>("ProviderKey")
.HasMaxLength(128)
.HasColumnType("varchar(128)");
b.Property<string>("ProviderDisplayName")
.HasColumnType("longtext");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("varchar(255)");
b.HasKey("LoginProvider", "ProviderKey");
b.HasIndex("UserId");
b.ToTable("AspNetUserLogins", (string)null);
b.HasOne("LEA.Models.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
{
b.Property<string>("UserId")
.HasColumnType("varchar(255)");
b.Property<string>("RoleId")
.HasColumnType("varchar(255)");
b.HasKey("UserId", "RoleId");
b.HasIndex("RoleId");
b.ToTable("AspNetUserRoles", (string)null);
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("LEA.Models.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{
b.Property<string>("UserId")
.HasColumnType("varchar(255)");
b.Property<string>("LoginProvider")
.HasMaxLength(128)
.HasColumnType("varchar(128)");
b.Property<string>("Name")
.HasMaxLength(128)
.HasColumnType("varchar(128)");
b.Property<string>("Value")
.HasColumnType("longtext");
b.HasKey("UserId", "LoginProvider", "Name");
b.ToTable("AspNetUserTokens", (string)null);
b.HasOne("LEA.Models.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
}
}
}

View File

@@ -0,0 +1,45 @@
using System.ComponentModel.DataAnnotations;
namespace LEA.Models;
public class Application
{
public int Id { get; set; }
[Required(ErrorMessage = "Bitte geben Sie eine Rolle oder Position an.")]
[StringLength(150, ErrorMessage = "Der Titel darf maximal {1} Zeichen lang sein.")]
[Display(Name = "Rolle / Position")]
public string Role { get; set; } = string.Empty;
[Required(ErrorMessage = "Bitte geben Sie ein Unternehmen an.")]
[StringLength(150, ErrorMessage = "Der Firmenname darf maximal {1} Zeichen lang sein.")]
[Display(Name = "Unternehmen")]
public string Company { get; set; } = string.Empty;
[StringLength(200, ErrorMessage = "Die Quelle darf maximal {1} Zeichen lang sein.")]
[Display(Name = "Quelle")]
public string? Source { get; set; }
[Display(Name = "Status")]
public ApplicationStatus Status { get; set; } = ApplicationStatus.Applied;
[DataType(DataType.Date)]
[Display(Name = "Beworben am")]
public DateTime AppliedOn { get; set; } = DateTime.Today;
[Display(Name = "Notizen")]
[DataType(DataType.MultilineText)]
public string? Notes { get; set; }
[Display(Name = "Letzte Aktualisierung")]
public DateTime UpdatedAt { get; set; } = DateTime.UtcNow;
[Required]
public string UserId { get; set; } = string.Empty;
[Display(Name = "Benutzer")]
public ApplicationUser? User { get; set; }
[Display(Name = "Ansprechpartner")]
public Contact? Contact { get; set; }
}

View File

@@ -0,0 +1,18 @@
using System.ComponentModel.DataAnnotations;
namespace LEA.Models;
public enum ApplicationStatus
{
[Display(Name = "Beworben")]
Applied,
[Display(Name = "Interview")]
Interview,
[Display(Name = "Angebot")]
Offer,
[Display(Name = "Abgelehnt")]
Rejected
}

View File

@@ -0,0 +1,15 @@
using System.ComponentModel.DataAnnotations;
using Microsoft.AspNetCore.Identity;
namespace LEA.Models;
public class ApplicationUser : IdentityUser
{
[Required(ErrorMessage = "Bitte geben Sie Ihren vollständigen Namen an.")]
[StringLength(100, ErrorMessage = "Der Name darf maximal {1} Zeichen lang sein.")]
[Display(Name = "Vollständiger Name")]
public string FullName { get; set; } = string.Empty;
[Display(Name = "Registriert am")]
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
}

View File

@@ -0,0 +1,24 @@
using System.ComponentModel.DataAnnotations;
namespace LEA.Models;
public class Contact
{
public int Id { get; set; }
[StringLength(100, ErrorMessage = "Der Name darf maximal {1} Zeichen lang sein.")]
[Display(Name = "Name")]
public string? FullName { get; set; }
[EmailAddress(ErrorMessage = "Bitte eine gültige E-Mail-Adresse eingeben.")]
[Display(Name = "E-Mail")]
public string? Email { get; set; }
[Phone(ErrorMessage = "Bitte eine gültige Telefonnummer eingeben.")]
[Display(Name = "Telefon")]
public string? Phone { get; set; }
public int ApplicationId { get; set; }
public Application Application { get; set; } = null!;
}

View File

@@ -0,0 +1,9 @@
namespace LEA.Models
{
public class ErrorViewModel
{
public string? RequestId { get; set; }
public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
}
}

View File

@@ -0,0 +1,63 @@
using LEA.Data;
using LEA.Models;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllersWithViews();
var connectionString = builder.Configuration.GetConnectionString("Default")
?? throw new InvalidOperationException("Keine gültige Datenbank-Verbindungszeichenfolge gefunden.");
builder.Services.AddDbContext<AppDbContext>(options =>
options.UseMySql(connectionString, ServerVersion.AutoDetect(connectionString)));
builder.Services.AddIdentity<ApplicationUser, IdentityRole>(options =>
{
options.Password.RequireDigit = false;
options.Password.RequireLowercase = true;
options.Password.RequireNonAlphanumeric = false;
options.Password.RequireUppercase = false;
options.Password.RequiredLength = 6;
options.User.RequireUniqueEmail = true;
})
.AddEntityFrameworkStores<AppDbContext>()
.AddDefaultTokenProviders();
builder.Services.ConfigureApplicationCookie(options =>
{
options.Cookie.Name = "LEA.Auth";
options.LoginPath = "/Account/Login";
options.AccessDeniedPath = "/Account/Login";
options.ExpireTimeSpan = TimeSpan.FromDays(14);
options.SlidingExpiration = true;
});
var app = builder.Build();
using (var scope = app.Services.CreateScope())
{
var dbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
dbContext.Database.Migrate();
}
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
app.Run();

View File

@@ -0,0 +1,38 @@
{
"$schema": "http://json.schemastore.org/launchsettings.json",
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:22165",
"sslPort": 44360
}
},
"profiles": {
"http": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"applicationUrl": "http://localhost:5102",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"https": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"applicationUrl": "https://localhost:7176;http://localhost:5102",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}

View File

@@ -0,0 +1,52 @@
using System.ComponentModel.DataAnnotations;
using LEA.Models;
using Microsoft.AspNetCore.Mvc.Rendering;
namespace LEA.ViewModels;
public class ApplicationFormViewModel
{
public int? Id { get; set; }
[Required(ErrorMessage = "Bitte geben Sie eine Rolle oder Position an.")]
[StringLength(150, ErrorMessage = "Der Titel darf maximal {1} Zeichen lang sein.")]
[Display(Name = "Rolle / Position")]
public string Role { get; set; } = string.Empty;
[Required(ErrorMessage = "Bitte geben Sie ein Unternehmen an.")]
[StringLength(150, ErrorMessage = "Der Firmenname darf maximal {1} Zeichen lang sein.")]
[Display(Name = "Unternehmen")]
public string Company { get; set; } = string.Empty;
[StringLength(200, ErrorMessage = "Die Quelle darf maximal {1} Zeichen lang sein.")]
[Display(Name = "Quelle")]
public string? Source { get; set; }
[Display(Name = "Status")]
public ApplicationStatus Status { get; set; } = ApplicationStatus.Applied;
[DataType(DataType.Date)]
[Display(Name = "Beworben am")]
public DateTime AppliedOn { get; set; } = DateTime.Today;
[Display(Name = "Notizen")]
[DataType(DataType.MultilineText)]
public string? Notes { get; set; }
[Display(Name = "Name")]
public string? ContactName { get; set; }
[EmailAddress(ErrorMessage = "Bitte eine gültige E-Mail-Adresse eingeben.")]
[Display(Name = "E-Mail")]
public string? ContactEmail { get; set; }
[Phone(ErrorMessage = "Bitte eine gültige Telefonnummer eingeben.")]
[Display(Name = "Telefon")]
public string? ContactPhone { get; set; }
public IEnumerable<SelectListItem> StatusOptions { get; set; } = Enumerable.Empty<SelectListItem>();
public string Heading { get; set; } = string.Empty;
public string SubmitText { get; set; } = string.Empty;
}

View File

@@ -0,0 +1,30 @@
using LEA.Models;
using Microsoft.AspNetCore.Mvc.Rendering;
namespace LEA.ViewModels;
public class ApplicationListViewModel
{
public IEnumerable<Application> Applications { get; set; } = new List<Application>();
public string? SearchTerm { get; set; }
public ApplicationStatus? StatusFilter { get; set; }
public IEnumerable<SelectListItem> StatusOptions { get; set; } = new List<SelectListItem>();
public IDictionary<ApplicationStatus, int> StatusCounts { get; set; } = new Dictionary<ApplicationStatus, int>();
public int TotalApplications { get; set; }
public int AppliedCount => GetCount(ApplicationStatus.Applied);
public int InterviewCount => GetCount(ApplicationStatus.Interview);
public int OfferCount => GetCount(ApplicationStatus.Offer);
public int RejectedCount => GetCount(ApplicationStatus.Rejected);
private int GetCount(ApplicationStatus status)
=> StatusCounts.TryGetValue(status, out var count) ? count : 0;
}

View File

@@ -0,0 +1,27 @@
using System.Collections.Generic;
using LEA.Models;
namespace LEA.ViewModels;
public class DashboardViewModel
{
public bool IsAuthenticated { get; set; }
public string? FullName { get; set; }
public int TotalApplications { get; set; }
public int AppliedCount { get; set; }
public int InterviewCount { get; set; }
public int OfferCount { get; set; }
public int RejectedCount { get; set; }
public IEnumerable<Application> RecentApplications { get; set; } = new List<Application>();
public IList<MonthlyApplicationStat> MonthlyApplications { get; set; } = new List<MonthlyApplicationStat>();
}
public record MonthlyApplicationStat(string Label, int Count);

View File

@@ -0,0 +1,24 @@
using System.ComponentModel.DataAnnotations;
using Microsoft.AspNetCore.Mvc;
namespace LEA.ViewModels;
public class LoginViewModel
{
[Required(ErrorMessage = "Bitte geben Sie eine E-Mail-Adresse ein.")]
[EmailAddress(ErrorMessage = "Bitte eine gültige E-Mail-Adresse eingeben.")]
[DataType(DataType.EmailAddress)]
[Display(Name = "E-Mail-Adresse")]
public string Email { get; set; } = string.Empty;
[Required(ErrorMessage = "Bitte geben Sie ein Passwort ein.")]
[DataType(DataType.Password)]
[Display(Name = "Passwort")]
public string Password { get; set; } = string.Empty;
[Display(Name = "Angemeldet bleiben")]
public bool RememberMe { get; set; }
[HiddenInput]
public string? ReturnUrl { get; set; }
}

View File

@@ -0,0 +1,29 @@
using System.ComponentModel.DataAnnotations;
namespace LEA.ViewModels;
public class RegisterViewModel
{
[Required(ErrorMessage = "Bitte geben Sie Ihren vollständigen Namen an.")]
[StringLength(100, ErrorMessage = "Der Name darf maximal {1} Zeichen lang sein.")]
[Display(Name = "Vollständiger Name")]
public string FullName { get; set; } = string.Empty;
[Required(ErrorMessage = "Bitte geben Sie eine E-Mail-Adresse ein.")]
[EmailAddress(ErrorMessage = "Bitte eine gültige E-Mail-Adresse eingeben.")]
[DataType(DataType.EmailAddress)]
[Display(Name = "E-Mail-Adresse")]
public string Email { get; set; } = string.Empty;
[Required(ErrorMessage = "Bitte geben Sie ein Passwort ein.")]
[DataType(DataType.Password)]
[StringLength(100, ErrorMessage = "Das Passwort muss mindestens {2} Zeichen enthalten.", MinimumLength = 6)]
[Display(Name = "Passwort")]
public string Password { get; set; } = string.Empty;
[Required(ErrorMessage = "Bitte wiederholen Sie das Passwort.")]
[DataType(DataType.Password)]
[Compare(nameof(Password), ErrorMessage = "Die Passwörter stimmen nicht überein.")]
[Display(Name = "Passwort bestätigen")]
public string ConfirmPassword { get; set; } = string.Empty;
}

View File

@@ -0,0 +1,45 @@
@model LoginViewModel
@{
ViewData["Title"] = "Anmelden";
}
<section class="auth-container">
<div class="auth-card">
<h1>Willkommen zurück</h1>
<p class="lead">Melde dich an, um deine Bewerbungen zu verwalten.</p>
<form asp-action="Login" method="post" class="form-grid">
@Html.AntiForgeryToken()
<input type="hidden" asp-for="ReturnUrl" />
<div asp-validation-summary="ModelOnly" class="validation-summary"></div>
<div class="form-group">
<label asp-for="Email"></label>
<input asp-for="Email" />
<span asp-validation-for="Email" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Password"></label>
<input asp-for="Password" />
<span asp-validation-for="Password" class="text-danger"></span>
</div>
<div class="form-group form-check">
<input asp-for="RememberMe" />
<label asp-for="RememberMe"></label>
</div>
<button type="submit" class="btn btn-primary">Anmelden</button>
</form>
<p class="auth-switch">
Noch kein Konto? <a asp-action="Register">Jetzt registrieren</a>
</p>
</div>
</section>
@section Scripts
{
<partial name="_ValidationScriptsPartial" />
}

View File

@@ -0,0 +1,51 @@
@model RegisterViewModel
@{
ViewData["Title"] = "Registrieren";
}
<section class="auth-container">
<div class="auth-card">
<h1>Account erstellen</h1>
<p class="lead">Lege dein persönliches Profil an und verwalte alle Bewerbungen zentral.</p>
<form asp-action="Register" method="post" class="form-grid">
@Html.AntiForgeryToken()
<div asp-validation-summary="ModelOnly" class="validation-summary"></div>
<div class="form-group">
<label asp-for="FullName"></label>
<input asp-for="FullName" />
<span asp-validation-for="FullName" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Email"></label>
<input asp-for="Email" />
<span asp-validation-for="Email" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Password"></label>
<input asp-for="Password" />
<span asp-validation-for="Password" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="ConfirmPassword"></label>
<input asp-for="ConfirmPassword" />
<span asp-validation-for="ConfirmPassword" class="text-danger"></span>
</div>
<button type="submit" class="btn btn-primary">Registrieren</button>
</form>
<p class="auth-switch">
Bereits ein Konto? <a asp-action="Login">Jetzt anmelden</a>
</p>
</div>
</section>
@section Scripts
{
<partial name="_ValidationScriptsPartial" />
}

View File

@@ -0,0 +1,28 @@
@model ApplicationFormViewModel
@{
ViewData["Title"] = "Bewerbung hinzufügen";
}
<section class="content-card">
<div class="section-header">
<h1>@Model.Heading</h1>
<a class="link" asp-action="Index">Zurück zur Übersicht</a>
</div>
<form asp-action="Create" method="post">
@Html.AntiForgeryToken()
<div asp-validation-summary="ModelOnly" class="validation-summary"></div>
<partial name="_ApplicationForm" />
<div class="form-actions">
<a class="btn btn-outline" asp-action="Index">Abbrechen</a>
<button type="submit" class="btn btn-primary">@Model.SubmitText</button>
</div>
</form>
</section>
@section Scripts
{
<partial name="_ValidationScriptsPartial" />
}

View File

@@ -0,0 +1,88 @@
@model Application
@{
ViewData["Title"] = "Details";
}
<section class="content-card">
<div class="section-header">
<h1>@Model.Role bei @Model.Company</h1>
<div class="action-group">
<a class="btn btn-outline" asp-action="Edit" asp-route-id="@Model.Id">Bearbeiten</a>
<a class="link" asp-action="Index">Zurück</a>
</div>
</div>
<div class="detail-grid">
<div class="detail-card">
<h2>Überblick</h2>
<dl>
<div>
<dt>Status</dt>
<dd>
<span class="status-badge status-@Model.Status.ToString().ToLowerInvariant()">
@Model.Status.GetDisplayName()
</span>
</dd>
</div>
<div>
<dt>Beworben am</dt>
<dd>@Model.AppliedOn.ToString("dd.MM.yyyy")</dd>
</div>
<div>
<dt>Quelle</dt>
<dd>@(string.IsNullOrWhiteSpace(Model.Source) ? "-" : Model.Source)</dd>
</div>
<div>
<dt>Zuletzt aktualisiert</dt>
<dd>@Model.UpdatedAt.ToLocalTime().ToString("dd.MM.yyyy HH:mm")</dd>
</div>
</dl>
</div>
<div class="detail-card">
<h2>Notizen</h2>
@if (string.IsNullOrWhiteSpace(Model.Notes))
{
<p class="muted">Keine Notizen hinterlegt.</p>
}
else
{
<p>@Model.Notes</p>
}
</div>
<div class="detail-card">
<h2>Ansprechpartner</h2>
@if (Model.Contact == null || (string.IsNullOrWhiteSpace(Model.Contact.FullName) && string.IsNullOrWhiteSpace(Model.Contact.Email) && string.IsNullOrWhiteSpace(Model.Contact.Phone)))
{
<p class="muted">Kein Ansprechpartner hinterlegt.</p>
}
else
{
<dl>
@if (!string.IsNullOrWhiteSpace(Model.Contact.FullName))
{
<div>
<dt>Name</dt>
<dd>@Model.Contact.FullName</dd>
</div>
}
@if (!string.IsNullOrWhiteSpace(Model.Contact.Email))
{
<div>
<dt>E-Mail</dt>
<dd><a href="mailto:@Model.Contact.Email">@Model.Contact.Email</a></dd>
</div>
}
@if (!string.IsNullOrWhiteSpace(Model.Contact.Phone))
{
<div>
<dt>Telefon</dt>
<dd><a href="tel:@Model.Contact.Phone">@Model.Contact.Phone</a></dd>
</div>
}
</dl>
}
</div>
</div>
</section>

View File

@@ -0,0 +1,28 @@
@model ApplicationFormViewModel
@{
ViewData["Title"] = "Bewerbung bearbeiten";
}
<section class="content-card">
<div class="section-header">
<h1>@Model.Heading</h1>
<a class="link" asp-action="Index">Zurück zur Übersicht</a>
</div>
<form asp-action="Edit" asp-route-id="@Model.Id" method="post">
@Html.AntiForgeryToken()
<div asp-validation-summary="ModelOnly" class="validation-summary"></div>
<partial name="_ApplicationForm" />
<div class="form-actions">
<a class="btn btn-outline" asp-action="Index">Abbrechen</a>
<button type="submit" class="btn btn-primary">@Model.SubmitText</button>
</div>
</form>
</section>
@section Scripts
{
<partial name="_ValidationScriptsPartial" />
}

View File

@@ -0,0 +1,142 @@
@model ApplicationListViewModel
@using System.Text.Json
@{
ViewData["Title"] = "Meine Bewerbungen";
}
<section class="page-header">
<div>
<h1>Bewerbungen</h1>
<p class="lead">Verwalte deine Bewerbungen, halte Fortschritte fest und behalte alle Termine im Blick.</p>
</div>
<a class="btn btn-primary" asp-action="Create">Neue Bewerbung</a>
</section>
<section class="stats-grid compact">
<article class="stat-card">
<p class="stat-label">Gesamt</p>
<p class="stat-value">@Model.TotalApplications</p>
<p class="stat-caption">Gespeicherte Bewerbungen</p>
</article>
<article class="stat-card">
<p class="stat-label">Beworben</p>
<p class="stat-value">@Model.AppliedCount</p>
<p class="stat-caption">Offene Bewerbungen</p>
</article>
<article class="stat-card">
<p class="stat-label">Interviews</p>
<p class="stat-value">@Model.InterviewCount</p>
<p class="stat-caption">Anstehende Gespräche</p>
</article>
<article class="stat-card">
<p class="stat-label">Angebote</p>
<p class="stat-value">@Model.OfferCount</p>
<p class="stat-caption">Positive Rückmeldungen</p>
</article>
<article class="stat-card">
<p class="stat-label">Absagen</p>
<p class="stat-value">@Model.RejectedCount</p>
<p class="stat-caption">Abgeschlossene Bewerbungen</p>
</article>
</section>
@if (Model.TotalApplications > 0)
{
var statusChartConfig = JsonSerializer.Serialize(new
{
type = "doughnut",
labels = new[] { "Beworben", "Interview", "Angebot", "Abgelehnt" },
values = new[] { Model.AppliedCount, Model.InterviewCount, Model.OfferCount, Model.RejectedCount },
colors = new[] { "#4460f7", "#3ac0a0", "#ff9f43", "#ef476f" },
borderColor = "#ffffff",
datasetLabel = "Bewerbungen",
legend = true
});
<section class="visual-grid compact">
<article class="chart-card">
<div class="chart-card-header">
<h2>Statusübersicht</h2>
<p>Verteilung deiner Bewerbungen nach aktuellem Status.</p>
</div>
<div class="chart-wrapper">
<canvas id="applications-status-chart" data-chart='@Html.Raw(statusChartConfig)'></canvas>
</div>
</article>
</section>
}
<section class="filter-card">
<form method="get" class="filter-grid">
<div class="form-group">
<label for="searchTerm">Suche</label>
<input id="searchTerm" name="searchTerm" value="@Model.SearchTerm" placeholder="Nach Unternehmen, Rolle oder Notizen suchen" />
</div>
<div class="form-group">
<label for="status">Status</label>
<select id="status" name="status">
@foreach (var option in Model.StatusOptions)
{
<option value="@option.Value" selected="@(option.Selected ? "selected" : null)">@option.Text</option>
}
</select>
</div>
<div class="filter-actions">
<button type="submit" class="btn btn-primary">Filter anwenden</button>
<a class="btn btn-outline" asp-action="Index">Zurücksetzen</a>
</div>
</form>
</section>
@if (!Model.Applications.Any())
{
<div class="empty-state">
<p>Noch keine Bewerbungen erfasst.</p>
<a class="btn btn-primary" asp-action="Create">Jetzt Bewerbung hinzufügen</a>
</div>
}
else
{
<div class="table-responsive">
<table class="data-table">
<thead>
<tr>
<th>Unternehmen</th>
<th>Rolle</th>
<th>Quelle</th>
<th>Status</th>
<th>Beworben am</th>
<th>Letzte Aktualisierung</th>
<th></th>
</tr>
</thead>
<tbody>
@foreach (var application in Model.Applications)
{
<tr>
<td>@application.Company</td>
<td>@application.Role</td>
<td>@(string.IsNullOrWhiteSpace(application.Source) ? "-" : application.Source)</td>
<td>
<span class="status-badge status-@application.Status.ToString().ToLowerInvariant()">
@application.Status.GetDisplayName()
</span>
</td>
<td>@application.AppliedOn.ToString("dd.MM.yyyy")</td>
<td>@application.UpdatedAt.ToLocalTime().ToString("dd.MM.yyyy HH:mm")</td>
<td>
<div class="table-actions">
<a class="link" asp-action="Details" asp-route-id="@application.Id">Details</a>
<a class="link" asp-action="Edit" asp-route-id="@application.Id">Bearbeiten</a>
<form asp-action="Delete" asp-route-id="@application.Id" method="post" class="inline-form" onsubmit="return confirm('Möchtest du diese Bewerbung wirklich löschen?');">
@Html.AntiForgeryToken()
<button type="submit" class="btn-link">Löschen</button>
</form>
</div>
</td>
</tr>
}
</tbody>
</table>
</div>
}

View File

@@ -0,0 +1,56 @@
@model ApplicationFormViewModel
<div class="form-grid two-columns">
<div class="form-group">
<label asp-for="Company"></label>
<input asp-for="Company" />
<span asp-validation-for="Company" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Role"></label>
<input asp-for="Role" />
<span asp-validation-for="Role" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Source"></label>
<input asp-for="Source" />
<span asp-validation-for="Source" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="AppliedOn"></label>
<input asp-for="AppliedOn" type="date" />
<span asp-validation-for="AppliedOn" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Status"></label>
<select asp-for="Status" asp-items="Model.StatusOptions"></select>
<span asp-validation-for="Status" class="text-danger"></span>
</div>
<div class="form-group full-width">
<label asp-for="Notes"></label>
<textarea asp-for="Notes" rows="4"></textarea>
<span asp-validation-for="Notes" class="text-danger"></span>
</div>
</div>
<div class="form-divider">
<span>Ansprechpartner (optional)</span>
</div>
<div class="form-grid three-columns">
<div class="form-group">
<label asp-for="ContactName"></label>
<input asp-for="ContactName" />
<span asp-validation-for="ContactName" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="ContactEmail"></label>
<input asp-for="ContactEmail" />
<span asp-validation-for="ContactEmail" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="ContactPhone"></label>
<input asp-for="ContactPhone" />
<span asp-validation-for="ContactPhone" class="text-danger"></span>
</div>
</div>

View File

@@ -0,0 +1,210 @@
@model DashboardViewModel
@using System.Text.Encodings.Web
@using System.Text.Json
@using System.Linq
@{
ViewData["Title"] = "Übersicht";
var chartSerializerOptions = new JsonSerializerOptions(JsonSerializerDefaults.Web)
{
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
};
var monthlyStats = Model.MonthlyApplications ?? Array.Empty<MonthlyApplicationStat>();
var recentApplications = Model.RecentApplications ?? Enumerable.Empty<Application>();
}
@if (!Model.IsAuthenticated)
{
<section class="hero">
<div class="hero-content">
<p class="eyebrow">LEA Bewerbungs-Tracker</p>
<h1>Behalte deine Bewerbungen mühelos im Blick</h1>
<p class="lead">Erfasse Bewerbungen, aktualisiere Status und sammle Notizen an einem Ort. Mit wenigen Klicks bleibst du organisiert und bestens vorbereitet auf jedes Gespräch.</p>
<div class="hero-actions">
<a class="btn btn-primary" asp-controller="Account" asp-action="Register">Jetzt registrieren</a>
<a class="btn btn-outline" asp-controller="Account" asp-action="Login">Anmelden</a>
</div>
</div>
<div class="hero-visual" aria-hidden="true">
<div class="hero-card">
<span class="status-badge status-applied">Beworben</span>
<span class="status-badge status-interview">Interview</span>
<span class="status-badge status-offer">Angebot</span>
<span class="status-badge status-rejected">Abgelehnt</span>
</div>
<div class="hero-highlight">
<p><strong>Smarter Überblick</strong></p>
<p>Analysiere Trends, nutze Filter und führe strukturierte Gespräche mit deinem nächsten Arbeitgeber.</p>
</div>
</div>
</section>
<section class="feature-grid">
<article class="feature-card">
<h3>Bewerbungen speichern</h3>
<p>Lege jede Bewerbung mit wenigen Angaben an inklusive Ansprechpartner, Quelle und individuellen Notizen.</p>
</article>
<article class="feature-card">
<h3>Status &amp; Fortschritt</h3>
<p>Halte Termine und Feedback fest, aktualisiere den Status und bleibe immer einen Schritt voraus.</p>
</article>
<article class="feature-card">
<h3>Individuelle Statistiken</h3>
<p>Erhalte auf einen Blick, wie viele Bewerbungen offen, im Interview oder bereits abgeschlossen sind.</p>
</article>
</section>
}
else
{
<section class="dashboard-header">
<div>
<p class="eyebrow">Willkommen zurück</p>
<h1>@(string.IsNullOrWhiteSpace(Model.FullName) ? "Dein Bewerbungs-Dashboard" : $"Hallo, {Model.FullName}!")</h1>
<p class="lead">Behalte deine Bewerbungen im Blick, aktualisiere den Status und bereite dich optimal auf deine nächsten Schritte vor.</p>
<div class="action-group">
<a class="btn btn-primary" asp-controller="Applications" asp-action="Create">Neue Bewerbung erfassen</a>
<a class="btn btn-outline" asp-controller="Applications" asp-action="Index">Alle Bewerbungen anzeigen</a>
</div>
</div>
</section>
<section class="stats-grid">
<article class="stat-card">
<p class="stat-label">Gesamt</p>
<p class="stat-value">@Model.TotalApplications</p>
<p class="stat-caption">Bewerbungen insgesamt</p>
</article>
<article class="stat-card">
<p class="stat-label">Beworben</p>
<p class="stat-value">@Model.AppliedCount</p>
<p class="stat-caption">Offene Bewerbungen</p>
</article>
<article class="stat-card">
<p class="stat-label">Interviews</p>
<p class="stat-value">@Model.InterviewCount</p>
<p class="stat-caption">Geplante Gespräche</p>
</article>
<article class="stat-card">
<p class="stat-label">Angebote</p>
<p class="stat-value">@Model.OfferCount</p>
<p class="stat-caption">Erfolgreiche Angebote</p>
</article>
<article class="stat-card">
<p class="stat-label">Absagen</p>
<p class="stat-value">@Model.RejectedCount</p>
<p class="stat-caption">Erhaltene Rückmeldungen</p>
</article>
</section>
<section class="visual-grid">
<article class="chart-card">
<div class="chart-card-header">
<h2>Statusübersicht</h2>
<p>Wie sich deine Bewerbungen aktuell verteilen.</p>
</div>
@if (Model.TotalApplications == 0)
{
<p class="chart-empty">Sobald du Bewerbungen erfasst, erscheint hier eine Visualisierung.</p>
}
else
{
var statusChartConfig = JsonSerializer.Serialize(new
{
type = "doughnut",
labels = new[] { "Beworben", "Interview", "Angebot", "Abgelehnt" },
values = new[] { Model.AppliedCount, Model.InterviewCount, Model.OfferCount, Model.RejectedCount },
colors = new[] { "#4460f7", "#3ac0a0", "#ff9f43", "#ef476f" },
borderColor = "#ffffff",
datasetLabel = "Bewerbungen",
legend = true
}, chartSerializerOptions);
<div class="chart-wrapper">
<canvas id="dashboard-status-chart" data-chart='@Html.Raw(statusChartConfig)'></canvas>
</div>
}
</article>
<article class="chart-card">
<div class="chart-card-header">
<h2>Monatlicher Verlauf</h2>
<p>Wie viele Bewerbungen du in den letzten Monaten versendet hast.</p>
</div>
@if (!monthlyStats.Any(stat => stat.Count > 0))
{
<p class="chart-empty">Sobald Bewerbungen vorhanden sind, zeigen wir hier deinen Verlauf.</p>
}
else
{
var monthlyLabels = monthlyStats.Select(stat => stat.Label).ToArray();
var monthlyValues = monthlyStats.Select(stat => stat.Count).ToArray();
var monthlyChartConfig = JsonSerializer.Serialize(new
{
type = "line",
labels = monthlyLabels,
values = monthlyValues,
colors = new[] { "rgba(68, 96, 247, 0.20)" },
borderColor = "#4460f7",
datasetLabel = "Bewerbungen",
tension = 0.35,
fill = true,
legend = false
}, chartSerializerOptions);
<div class="chart-wrapper">
<canvas id="dashboard-monthly-chart" data-chart='@Html.Raw(monthlyChartConfig)'></canvas>
</div>
}
</article>
</section>
<section class="recent-applications">
<div class="section-header">
<h2>Aktuelle Bewerbungen</h2>
<a class="link" asp-controller="Applications" asp-action="Index">Zur Bewerbungsübersicht</a>
</div>
@if (!recentApplications.Any())
{
<div class="empty-state">
<p>Noch keine Bewerbungen erfasst.</p>
<a class="btn btn-primary" asp-controller="Applications" asp-action="Create">Jetzt starten</a>
</div>
}
else
{
<div class="table-responsive">
<table class="data-table">
<thead>
<tr>
<th>Unternehmen</th>
<th>Rolle</th>
<th>Status</th>
<th>Beworben am</th>
<th></th>
</tr>
</thead>
<tbody>
@foreach (var application in recentApplications)
{
<tr>
<td>@application.Company</td>
<td>@application.Role</td>
<td>
<span class="status-badge status-@application.Status.ToString().ToLowerInvariant()">
@application.Status.GetDisplayName()
</span>
</td>
<td>@application.AppliedOn.ToString("dd.MM.yyyy")</td>
<td class="table-actions">
<a class="link" asp-controller="Applications" asp-action="Details" asp-route-id="@application.Id">Details</a>
</td>
</tr>
}
</tbody>
</table>
</div>
}
</section>
}

View File

@@ -0,0 +1,16 @@
@{
ViewData["Title"] = "Datenschutz";
}
<section class="content-card">
<h1>Datenschutzhinweise</h1>
<p>
Der Schutz deiner persönlichen Daten ist uns wichtig. Diese Anwendung speichert ausschließlich die Angaben,
die du selbst für deine Bewerbungsverwaltung hinterlegst. Dazu gehören unter anderem Unternehmensdaten,
Ansprechpartner sowie deine Notizen.
</p>
<p>
Du kannst deine Daten jederzeit bearbeiten oder löschen. Für Fragen oder Feedback kontaktiere uns bitte über
die im Impressum hinterlegte E-Mail-Adresse.
</p>
</section>

View File

@@ -0,0 +1,25 @@
@model ErrorViewModel
@{
ViewData["Title"] = "Error";
}
<h1 class="text-danger">Error.</h1>
<h2 class="text-danger">An error occurred while processing your request.</h2>
@if (Model.ShowRequestId)
{
<p>
<strong>Request ID:</strong> <code>@Model.RequestId</code>
</p>
}
<h3>Development Mode</h3>
<p>
Swapping to <strong>Development</strong> environment will display more detailed information about the error that occurred.
</p>
<p>
<strong>The Development environment shouldn't be enabled for deployed applications.</strong>
It can result in displaying sensitive information from exceptions to end users.
For local debugging, enable the <strong>Development</strong> environment by setting the <strong>ASPNETCORE_ENVIRONMENT</strong> environment variable to <strong>Development</strong>
and restarting the app.
</p>

View File

@@ -0,0 +1,97 @@
@using LEA.Models
@inject UserManager<ApplicationUser> UserManager
@{
ApplicationUser? currentUser = null;
if (User.Identity?.IsAuthenticated ?? false)
{
currentUser = await UserManager.GetUserAsync(User);
}
var displayName = currentUser?.FullName;
}
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>@ViewData["Title"] - LEA Bewerbungs-Tracker</title>
<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=Inter:wght@400;500;600;700&display=swap" rel="stylesheet" />
<link rel="stylesheet" href="~/css/site.css" asp-append-version="true" />
<link rel="stylesheet" href="~/LEA.styles.css" asp-append-version="true" />
</head>
<body>
<header class="site-header">
<nav class="main-nav">
<a class="brand" asp-controller="Home" asp-action="Index">LEA Bewerbungs-Tracker</a>
<button class="nav-toggle" type="button" aria-label="Navigation umschalten" data-target="primary-nav">
<span></span>
<span></span>
<span></span>
</button>
<div class="nav-menu" id="primary-nav">
<ul class="nav-links">
<li><a asp-controller="Home" asp-action="Index" class="@(ViewContext.RouteData.Values["controller"]?.ToString() == "Home" ? "active" : null)">Start</a></li>
@if (User.Identity?.IsAuthenticated ?? false)
{
<li><a asp-controller="Applications" asp-action="Index" class="@(ViewContext.RouteData.Values["controller"]?.ToString() == "Applications" ? "active" : null)">Bewerbungen</a></li>
}
<li><a asp-controller="Home" asp-action="Privacy">Datenschutz</a></li>
</ul>
<div class="nav-actions">
@if (User.Identity?.IsAuthenticated ?? false)
{
<span class="nav-user">Hallo, @(string.IsNullOrWhiteSpace(displayName) ? User.Identity?.Name : displayName)</span>
<form asp-controller="Account" asp-action="Logout" method="post">
@Html.AntiForgeryToken()
<button type="submit" class="btn btn-outline">Abmelden</button>
</form>
}
else
{
<a class="btn btn-link" asp-controller="Account" asp-action="Login">Anmelden</a>
<a class="btn btn-primary" asp-controller="Account" asp-action="Register">Registrieren</a>
}
</div>
</div>
</nav>
</header>
<div class="flash-container">
@if (TempData["Success"] is string successMessage)
{
<div class="flash-message success" role="status">
<span>@successMessage</span>
<button type="button" class="flash-close" aria-label="Meldung schließen">&times;</button>
</div>
}
@if (TempData["Error"] is string errorMessage)
{
<div class="flash-message error" role="status">
<span>@errorMessage</span>
<button type="button" class="flash-close" aria-label="Meldung schließen">&times;</button>
</div>
}
</div>
<main class="site-main">
@RenderBody()
</main>
<footer class="site-footer">
<div class="footer-inner">
<p>&copy; @DateTime.Now.Year LEA Bewerbungs-Tracker</p>
<div class="footer-links">
<a asp-controller="Home" asp-action="Index">Start</a>
<a asp-controller="Home" asp-action="Privacy">Datenschutz</a>
</div>
</div>
</footer>
<script src="~/lib/jquery/dist/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js" crossorigin="anonymous"></script>
<script src="~/js/site.js" asp-append-version="true"></script>
@await RenderSectionAsync("Scripts", required: false)
</body>
</html>

View File

@@ -0,0 +1,48 @@
/* Please see documentation at https://learn.microsoft.com/aspnet/core/client-side/bundling-and-minification
for details on configuring this project to bundle and minify static web assets. */
a.navbar-brand {
white-space: normal;
text-align: center;
word-break: break-all;
}
a {
color: #0077cc;
}
.btn-primary {
color: #fff;
background-color: #1b6ec2;
border-color: #1861ac;
}
.nav-pills .nav-link.active, .nav-pills .show > .nav-link {
color: #fff;
background-color: #1b6ec2;
border-color: #1861ac;
}
.border-top {
border-top: 1px solid #e5e5e5;
}
.border-bottom {
border-bottom: 1px solid #e5e5e5;
}
.box-shadow {
box-shadow: 0 .25rem .75rem rgba(0, 0, 0, .05);
}
button.accept-policy {
font-size: 1rem;
line-height: inherit;
}
.footer {
position: absolute;
bottom: 0;
width: 100%;
white-space: nowrap;
line-height: 60px;
}

View File

@@ -0,0 +1,2 @@
<script src="~/lib/jquery-validation/dist/jquery.validate.min.js"></script>
<script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js"></script>

View File

@@ -0,0 +1,6 @@
@using LEA
@using LEA.Models
@using LEA.ViewModels
@using LEA.Extensions
@using Microsoft.AspNetCore.Identity
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

View File

@@ -0,0 +1,3 @@
@{
Layout = "_Layout";
}

View File

@@ -0,0 +1,8 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}

View File

@@ -0,0 +1,13 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"ConnectionStrings": {
"Default": "Server=localhost;Database=jobtracker;User=root;Password=08911395;TreatTinyAsBoolean=true;SslMode=None"
}
}

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,889 @@
{
"runtimeTarget": {
"name": ".NETCoreApp,Version=v8.0",
"signature": ""
},
"compilationOptions": {},
"targets": {
".NETCoreApp,Version=v8.0": {
"LEA/1.0.0": {
"dependencies": {
"Microsoft.AspNetCore.Identity.EntityFrameworkCore": "8.0.8",
"Microsoft.EntityFrameworkCore.Design": "8.0.8",
"Microsoft.EntityFrameworkCore.Tools": "8.0.8",
"MySqlConnector": "2.3.7",
"Pomelo.EntityFrameworkCore.MySql": "8.0.2"
},
"runtime": {
"LEA.dll": {}
}
},
"Humanizer.Core/2.14.1": {
"runtime": {
"lib/net6.0/Humanizer.dll": {
"assemblyVersion": "2.14.0.0",
"fileVersion": "2.14.1.48190"
}
}
},
"Microsoft.AspNetCore.Cryptography.Internal/8.0.8": {
"runtime": {
"lib/net8.0/Microsoft.AspNetCore.Cryptography.Internal.dll": {
"assemblyVersion": "8.0.0.0",
"fileVersion": "8.0.824.36908"
}
}
},
"Microsoft.AspNetCore.Cryptography.KeyDerivation/8.0.8": {
"dependencies": {
"Microsoft.AspNetCore.Cryptography.Internal": "8.0.8"
},
"runtime": {
"lib/net8.0/Microsoft.AspNetCore.Cryptography.KeyDerivation.dll": {
"assemblyVersion": "8.0.0.0",
"fileVersion": "8.0.824.36908"
}
}
},
"Microsoft.AspNetCore.Identity.EntityFrameworkCore/8.0.8": {
"dependencies": {
"Microsoft.EntityFrameworkCore.Relational": "8.0.8",
"Microsoft.Extensions.Identity.Stores": "8.0.8"
},
"runtime": {
"lib/net8.0/Microsoft.AspNetCore.Identity.EntityFrameworkCore.dll": {
"assemblyVersion": "8.0.8.0",
"fileVersion": "8.0.824.36908"
}
}
},
"Microsoft.Bcl.AsyncInterfaces/6.0.0": {
"runtime": {
"lib/netstandard2.1/Microsoft.Bcl.AsyncInterfaces.dll": {
"assemblyVersion": "6.0.0.0",
"fileVersion": "6.0.21.52210"
}
}
},
"Microsoft.CodeAnalysis.Analyzers/3.3.3": {},
"Microsoft.CodeAnalysis.Common/4.5.0": {
"dependencies": {
"Microsoft.CodeAnalysis.Analyzers": "3.3.3",
"System.Collections.Immutable": "6.0.0",
"System.Reflection.Metadata": "6.0.1",
"System.Runtime.CompilerServices.Unsafe": "6.0.0",
"System.Text.Encoding.CodePages": "6.0.0"
},
"runtime": {
"lib/netcoreapp3.1/Microsoft.CodeAnalysis.dll": {
"assemblyVersion": "4.5.0.0",
"fileVersion": "4.500.23.10905"
}
},
"resources": {
"lib/netcoreapp3.1/cs/Microsoft.CodeAnalysis.resources.dll": {
"locale": "cs"
},
"lib/netcoreapp3.1/de/Microsoft.CodeAnalysis.resources.dll": {
"locale": "de"
},
"lib/netcoreapp3.1/es/Microsoft.CodeAnalysis.resources.dll": {
"locale": "es"
},
"lib/netcoreapp3.1/fr/Microsoft.CodeAnalysis.resources.dll": {
"locale": "fr"
},
"lib/netcoreapp3.1/it/Microsoft.CodeAnalysis.resources.dll": {
"locale": "it"
},
"lib/netcoreapp3.1/ja/Microsoft.CodeAnalysis.resources.dll": {
"locale": "ja"
},
"lib/netcoreapp3.1/ko/Microsoft.CodeAnalysis.resources.dll": {
"locale": "ko"
},
"lib/netcoreapp3.1/pl/Microsoft.CodeAnalysis.resources.dll": {
"locale": "pl"
},
"lib/netcoreapp3.1/pt-BR/Microsoft.CodeAnalysis.resources.dll": {
"locale": "pt-BR"
},
"lib/netcoreapp3.1/ru/Microsoft.CodeAnalysis.resources.dll": {
"locale": "ru"
},
"lib/netcoreapp3.1/tr/Microsoft.CodeAnalysis.resources.dll": {
"locale": "tr"
},
"lib/netcoreapp3.1/zh-Hans/Microsoft.CodeAnalysis.resources.dll": {
"locale": "zh-Hans"
},
"lib/netcoreapp3.1/zh-Hant/Microsoft.CodeAnalysis.resources.dll": {
"locale": "zh-Hant"
}
}
},
"Microsoft.CodeAnalysis.CSharp/4.5.0": {
"dependencies": {
"Microsoft.CodeAnalysis.Common": "4.5.0"
},
"runtime": {
"lib/netcoreapp3.1/Microsoft.CodeAnalysis.CSharp.dll": {
"assemblyVersion": "4.5.0.0",
"fileVersion": "4.500.23.10905"
}
},
"resources": {
"lib/netcoreapp3.1/cs/Microsoft.CodeAnalysis.CSharp.resources.dll": {
"locale": "cs"
},
"lib/netcoreapp3.1/de/Microsoft.CodeAnalysis.CSharp.resources.dll": {
"locale": "de"
},
"lib/netcoreapp3.1/es/Microsoft.CodeAnalysis.CSharp.resources.dll": {
"locale": "es"
},
"lib/netcoreapp3.1/fr/Microsoft.CodeAnalysis.CSharp.resources.dll": {
"locale": "fr"
},
"lib/netcoreapp3.1/it/Microsoft.CodeAnalysis.CSharp.resources.dll": {
"locale": "it"
},
"lib/netcoreapp3.1/ja/Microsoft.CodeAnalysis.CSharp.resources.dll": {
"locale": "ja"
},
"lib/netcoreapp3.1/ko/Microsoft.CodeAnalysis.CSharp.resources.dll": {
"locale": "ko"
},
"lib/netcoreapp3.1/pl/Microsoft.CodeAnalysis.CSharp.resources.dll": {
"locale": "pl"
},
"lib/netcoreapp3.1/pt-BR/Microsoft.CodeAnalysis.CSharp.resources.dll": {
"locale": "pt-BR"
},
"lib/netcoreapp3.1/ru/Microsoft.CodeAnalysis.CSharp.resources.dll": {
"locale": "ru"
},
"lib/netcoreapp3.1/tr/Microsoft.CodeAnalysis.CSharp.resources.dll": {
"locale": "tr"
},
"lib/netcoreapp3.1/zh-Hans/Microsoft.CodeAnalysis.CSharp.resources.dll": {
"locale": "zh-Hans"
},
"lib/netcoreapp3.1/zh-Hant/Microsoft.CodeAnalysis.CSharp.resources.dll": {
"locale": "zh-Hant"
}
}
},
"Microsoft.CodeAnalysis.CSharp.Workspaces/4.5.0": {
"dependencies": {
"Humanizer.Core": "2.14.1",
"Microsoft.CodeAnalysis.CSharp": "4.5.0",
"Microsoft.CodeAnalysis.Common": "4.5.0",
"Microsoft.CodeAnalysis.Workspaces.Common": "4.5.0"
},
"runtime": {
"lib/netcoreapp3.1/Microsoft.CodeAnalysis.CSharp.Workspaces.dll": {
"assemblyVersion": "4.5.0.0",
"fileVersion": "4.500.23.10905"
}
},
"resources": {
"lib/netcoreapp3.1/cs/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll": {
"locale": "cs"
},
"lib/netcoreapp3.1/de/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll": {
"locale": "de"
},
"lib/netcoreapp3.1/es/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll": {
"locale": "es"
},
"lib/netcoreapp3.1/fr/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll": {
"locale": "fr"
},
"lib/netcoreapp3.1/it/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll": {
"locale": "it"
},
"lib/netcoreapp3.1/ja/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll": {
"locale": "ja"
},
"lib/netcoreapp3.1/ko/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll": {
"locale": "ko"
},
"lib/netcoreapp3.1/pl/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll": {
"locale": "pl"
},
"lib/netcoreapp3.1/pt-BR/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll": {
"locale": "pt-BR"
},
"lib/netcoreapp3.1/ru/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll": {
"locale": "ru"
},
"lib/netcoreapp3.1/tr/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll": {
"locale": "tr"
},
"lib/netcoreapp3.1/zh-Hans/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll": {
"locale": "zh-Hans"
},
"lib/netcoreapp3.1/zh-Hant/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll": {
"locale": "zh-Hant"
}
}
},
"Microsoft.CodeAnalysis.Workspaces.Common/4.5.0": {
"dependencies": {
"Humanizer.Core": "2.14.1",
"Microsoft.Bcl.AsyncInterfaces": "6.0.0",
"Microsoft.CodeAnalysis.Common": "4.5.0",
"System.Composition": "6.0.0",
"System.IO.Pipelines": "6.0.3",
"System.Threading.Channels": "6.0.0"
},
"runtime": {
"lib/netcoreapp3.1/Microsoft.CodeAnalysis.Workspaces.dll": {
"assemblyVersion": "4.5.0.0",
"fileVersion": "4.500.23.10905"
}
},
"resources": {
"lib/netcoreapp3.1/cs/Microsoft.CodeAnalysis.Workspaces.resources.dll": {
"locale": "cs"
},
"lib/netcoreapp3.1/de/Microsoft.CodeAnalysis.Workspaces.resources.dll": {
"locale": "de"
},
"lib/netcoreapp3.1/es/Microsoft.CodeAnalysis.Workspaces.resources.dll": {
"locale": "es"
},
"lib/netcoreapp3.1/fr/Microsoft.CodeAnalysis.Workspaces.resources.dll": {
"locale": "fr"
},
"lib/netcoreapp3.1/it/Microsoft.CodeAnalysis.Workspaces.resources.dll": {
"locale": "it"
},
"lib/netcoreapp3.1/ja/Microsoft.CodeAnalysis.Workspaces.resources.dll": {
"locale": "ja"
},
"lib/netcoreapp3.1/ko/Microsoft.CodeAnalysis.Workspaces.resources.dll": {
"locale": "ko"
},
"lib/netcoreapp3.1/pl/Microsoft.CodeAnalysis.Workspaces.resources.dll": {
"locale": "pl"
},
"lib/netcoreapp3.1/pt-BR/Microsoft.CodeAnalysis.Workspaces.resources.dll": {
"locale": "pt-BR"
},
"lib/netcoreapp3.1/ru/Microsoft.CodeAnalysis.Workspaces.resources.dll": {
"locale": "ru"
},
"lib/netcoreapp3.1/tr/Microsoft.CodeAnalysis.Workspaces.resources.dll": {
"locale": "tr"
},
"lib/netcoreapp3.1/zh-Hans/Microsoft.CodeAnalysis.Workspaces.resources.dll": {
"locale": "zh-Hans"
},
"lib/netcoreapp3.1/zh-Hant/Microsoft.CodeAnalysis.Workspaces.resources.dll": {
"locale": "zh-Hant"
}
}
},
"Microsoft.EntityFrameworkCore/8.0.8": {
"dependencies": {
"Microsoft.EntityFrameworkCore.Abstractions": "8.0.8",
"Microsoft.EntityFrameworkCore.Analyzers": "8.0.8",
"Microsoft.Extensions.Caching.Memory": "8.0.0",
"Microsoft.Extensions.Logging": "8.0.0"
},
"runtime": {
"lib/net8.0/Microsoft.EntityFrameworkCore.dll": {
"assemblyVersion": "8.0.8.0",
"fileVersion": "8.0.824.36704"
}
}
},
"Microsoft.EntityFrameworkCore.Abstractions/8.0.8": {
"runtime": {
"lib/net8.0/Microsoft.EntityFrameworkCore.Abstractions.dll": {
"assemblyVersion": "8.0.8.0",
"fileVersion": "8.0.824.36704"
}
}
},
"Microsoft.EntityFrameworkCore.Analyzers/8.0.8": {},
"Microsoft.EntityFrameworkCore.Design/8.0.8": {
"dependencies": {
"Humanizer.Core": "2.14.1",
"Microsoft.CodeAnalysis.CSharp.Workspaces": "4.5.0",
"Microsoft.EntityFrameworkCore.Relational": "8.0.8",
"Microsoft.Extensions.DependencyModel": "8.0.1",
"Mono.TextTemplating": "2.2.1"
},
"runtime": {
"lib/net8.0/Microsoft.EntityFrameworkCore.Design.dll": {
"assemblyVersion": "8.0.8.0",
"fileVersion": "8.0.824.36704"
}
}
},
"Microsoft.EntityFrameworkCore.Relational/8.0.8": {
"dependencies": {
"Microsoft.EntityFrameworkCore": "8.0.8",
"Microsoft.Extensions.Configuration.Abstractions": "8.0.0"
},
"runtime": {
"lib/net8.0/Microsoft.EntityFrameworkCore.Relational.dll": {
"assemblyVersion": "8.0.8.0",
"fileVersion": "8.0.824.36704"
}
}
},
"Microsoft.EntityFrameworkCore.Tools/8.0.8": {
"dependencies": {
"Microsoft.EntityFrameworkCore.Design": "8.0.8"
}
},
"Microsoft.Extensions.Caching.Abstractions/8.0.0": {
"dependencies": {
"Microsoft.Extensions.Primitives": "8.0.0"
}
},
"Microsoft.Extensions.Caching.Memory/8.0.0": {
"dependencies": {
"Microsoft.Extensions.Caching.Abstractions": "8.0.0",
"Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0",
"Microsoft.Extensions.Logging.Abstractions": "8.0.0",
"Microsoft.Extensions.Options": "8.0.2",
"Microsoft.Extensions.Primitives": "8.0.0"
}
},
"Microsoft.Extensions.Configuration.Abstractions/8.0.0": {
"dependencies": {
"Microsoft.Extensions.Primitives": "8.0.0"
}
},
"Microsoft.Extensions.DependencyInjection/8.0.0": {
"dependencies": {
"Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0"
}
},
"Microsoft.Extensions.DependencyInjection.Abstractions/8.0.0": {},
"Microsoft.Extensions.DependencyModel/8.0.1": {
"dependencies": {
"System.Text.Encodings.Web": "8.0.0",
"System.Text.Json": "8.0.4"
},
"runtime": {
"lib/net8.0/Microsoft.Extensions.DependencyModel.dll": {
"assemblyVersion": "8.0.0.1",
"fileVersion": "8.0.724.31311"
}
}
},
"Microsoft.Extensions.Identity.Core/8.0.8": {
"dependencies": {
"Microsoft.AspNetCore.Cryptography.KeyDerivation": "8.0.8",
"Microsoft.Extensions.Logging": "8.0.0",
"Microsoft.Extensions.Options": "8.0.2"
},
"runtime": {
"lib/net8.0/Microsoft.Extensions.Identity.Core.dll": {
"assemblyVersion": "8.0.0.0",
"fileVersion": "8.0.824.36908"
}
}
},
"Microsoft.Extensions.Identity.Stores/8.0.8": {
"dependencies": {
"Microsoft.Extensions.Caching.Abstractions": "8.0.0",
"Microsoft.Extensions.Identity.Core": "8.0.8",
"Microsoft.Extensions.Logging": "8.0.0"
},
"runtime": {
"lib/net8.0/Microsoft.Extensions.Identity.Stores.dll": {
"assemblyVersion": "8.0.0.0",
"fileVersion": "8.0.824.36908"
}
}
},
"Microsoft.Extensions.Logging/8.0.0": {
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "8.0.0",
"Microsoft.Extensions.Logging.Abstractions": "8.0.0",
"Microsoft.Extensions.Options": "8.0.2"
}
},
"Microsoft.Extensions.Logging.Abstractions/8.0.0": {
"dependencies": {
"Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0"
}
},
"Microsoft.Extensions.Options/8.0.2": {
"dependencies": {
"Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0",
"Microsoft.Extensions.Primitives": "8.0.0"
},
"runtime": {
"lib/net8.0/Microsoft.Extensions.Options.dll": {
"assemblyVersion": "8.0.0.0",
"fileVersion": "8.0.224.6711"
}
}
},
"Microsoft.Extensions.Primitives/8.0.0": {},
"Mono.TextTemplating/2.2.1": {
"dependencies": {
"System.CodeDom": "4.4.0"
},
"runtime": {
"lib/netstandard2.0/Mono.TextTemplating.dll": {
"assemblyVersion": "2.2.0.0",
"fileVersion": "2.2.1.1"
}
}
},
"MySqlConnector/2.3.7": {
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "8.0.0"
},
"runtime": {
"lib/net8.0/MySqlConnector.dll": {
"assemblyVersion": "2.0.0.0",
"fileVersion": "2.3.7.0"
}
}
},
"Pomelo.EntityFrameworkCore.MySql/8.0.2": {
"dependencies": {
"Microsoft.EntityFrameworkCore.Relational": "8.0.8",
"MySqlConnector": "2.3.7"
},
"runtime": {
"lib/net8.0/Pomelo.EntityFrameworkCore.MySql.dll": {
"assemblyVersion": "8.0.2.0",
"fileVersion": "8.0.2.0"
}
}
},
"System.CodeDom/4.4.0": {
"runtime": {
"lib/netstandard2.0/System.CodeDom.dll": {
"assemblyVersion": "4.0.0.0",
"fileVersion": "4.6.25519.3"
}
}
},
"System.Collections.Immutable/6.0.0": {
"dependencies": {
"System.Runtime.CompilerServices.Unsafe": "6.0.0"
}
},
"System.Composition/6.0.0": {
"dependencies": {
"System.Composition.AttributedModel": "6.0.0",
"System.Composition.Convention": "6.0.0",
"System.Composition.Hosting": "6.0.0",
"System.Composition.Runtime": "6.0.0",
"System.Composition.TypedParts": "6.0.0"
}
},
"System.Composition.AttributedModel/6.0.0": {
"runtime": {
"lib/net6.0/System.Composition.AttributedModel.dll": {
"assemblyVersion": "6.0.0.0",
"fileVersion": "6.0.21.52210"
}
}
},
"System.Composition.Convention/6.0.0": {
"dependencies": {
"System.Composition.AttributedModel": "6.0.0"
},
"runtime": {
"lib/net6.0/System.Composition.Convention.dll": {
"assemblyVersion": "6.0.0.0",
"fileVersion": "6.0.21.52210"
}
}
},
"System.Composition.Hosting/6.0.0": {
"dependencies": {
"System.Composition.Runtime": "6.0.0"
},
"runtime": {
"lib/net6.0/System.Composition.Hosting.dll": {
"assemblyVersion": "6.0.0.0",
"fileVersion": "6.0.21.52210"
}
}
},
"System.Composition.Runtime/6.0.0": {
"runtime": {
"lib/net6.0/System.Composition.Runtime.dll": {
"assemblyVersion": "6.0.0.0",
"fileVersion": "6.0.21.52210"
}
}
},
"System.Composition.TypedParts/6.0.0": {
"dependencies": {
"System.Composition.AttributedModel": "6.0.0",
"System.Composition.Hosting": "6.0.0",
"System.Composition.Runtime": "6.0.0"
},
"runtime": {
"lib/net6.0/System.Composition.TypedParts.dll": {
"assemblyVersion": "6.0.0.0",
"fileVersion": "6.0.21.52210"
}
}
},
"System.IO.Pipelines/6.0.3": {},
"System.Reflection.Metadata/6.0.1": {
"dependencies": {
"System.Collections.Immutable": "6.0.0"
}
},
"System.Runtime.CompilerServices.Unsafe/6.0.0": {},
"System.Text.Encoding.CodePages/6.0.0": {
"dependencies": {
"System.Runtime.CompilerServices.Unsafe": "6.0.0"
}
},
"System.Text.Encodings.Web/8.0.0": {},
"System.Text.Json/8.0.4": {
"dependencies": {
"System.Text.Encodings.Web": "8.0.0"
}
},
"System.Threading.Channels/6.0.0": {}
}
},
"libraries": {
"LEA/1.0.0": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Humanizer.Core/2.14.1": {
"type": "package",
"serviceable": true,
"sha512": "sha512-lQKvtaTDOXnoVJ20ibTuSIOf2i0uO0MPbDhd1jm238I+U/2ZnRENj0cktKZhtchBMtCUSRQ5v4xBCUbKNmyVMw==",
"path": "humanizer.core/2.14.1",
"hashPath": "humanizer.core.2.14.1.nupkg.sha512"
},
"Microsoft.AspNetCore.Cryptography.Internal/8.0.8": {
"type": "package",
"serviceable": true,
"sha512": "sha512-VrfHrQwvj8p5W3z+PhHCkUMgpPnThJ9ZDeu+AJrFGWFvHSA6sI7iP/k9LjqkdFXsFezlOOZWNAgB6EH9gCCA4g==",
"path": "microsoft.aspnetcore.cryptography.internal/8.0.8",
"hashPath": "microsoft.aspnetcore.cryptography.internal.8.0.8.nupkg.sha512"
},
"Microsoft.AspNetCore.Cryptography.KeyDerivation/8.0.8": {
"type": "package",
"serviceable": true,
"sha512": "sha512-Ct73TxRAPhQ8OqckzgIUPjJ1/WXnQbOnGjqbvTyPmMptdOg9YJ9wXQh9U+da2s6nki54mcZAyonvaKXLsrXhKQ==",
"path": "microsoft.aspnetcore.cryptography.keyderivation/8.0.8",
"hashPath": "microsoft.aspnetcore.cryptography.keyderivation.8.0.8.nupkg.sha512"
},
"Microsoft.AspNetCore.Identity.EntityFrameworkCore/8.0.8": {
"type": "package",
"serviceable": true,
"sha512": "sha512-OtjXwZkWdW7UTQ1XR/IGWmeVCujnly0fjjCbUxsjHC1o3cB9iWQRTREdL4931Ulwk9RRgjYQ/mAo7k8ggcZ9xQ==",
"path": "microsoft.aspnetcore.identity.entityframeworkcore/8.0.8",
"hashPath": "microsoft.aspnetcore.identity.entityframeworkcore.8.0.8.nupkg.sha512"
},
"Microsoft.Bcl.AsyncInterfaces/6.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-UcSjPsst+DfAdJGVDsu346FX0ci0ah+lw3WRtn18NUwEqRt70HaOQ7lI72vy3+1LxtqI3T5GWwV39rQSrCzAeg==",
"path": "microsoft.bcl.asyncinterfaces/6.0.0",
"hashPath": "microsoft.bcl.asyncinterfaces.6.0.0.nupkg.sha512"
},
"Microsoft.CodeAnalysis.Analyzers/3.3.3": {
"type": "package",
"serviceable": true,
"sha512": "sha512-j/rOZtLMVJjrfLRlAMckJLPW/1rze9MT1yfWqSIbUPGRu1m1P0fuo9PmqapwsmePfGB5PJrudQLvmUOAMF0DqQ==",
"path": "microsoft.codeanalysis.analyzers/3.3.3",
"hashPath": "microsoft.codeanalysis.analyzers.3.3.3.nupkg.sha512"
},
"Microsoft.CodeAnalysis.Common/4.5.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-lwAbIZNdnY0SUNoDmZHkVUwLO8UyNnyyh1t/4XsbFxi4Ounb3xszIYZaWhyj5ZjyfcwqwmtMbE7fUTVCqQEIdQ==",
"path": "microsoft.codeanalysis.common/4.5.0",
"hashPath": "microsoft.codeanalysis.common.4.5.0.nupkg.sha512"
},
"Microsoft.CodeAnalysis.CSharp/4.5.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-cM59oMKAOxvdv76bdmaKPy5hfj+oR+zxikWoueEB7CwTko7mt9sVKZI8Qxlov0C/LuKEG+WQwifepqL3vuTiBQ==",
"path": "microsoft.codeanalysis.csharp/4.5.0",
"hashPath": "microsoft.codeanalysis.csharp.4.5.0.nupkg.sha512"
},
"Microsoft.CodeAnalysis.CSharp.Workspaces/4.5.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-h74wTpmGOp4yS4hj+EvNzEiPgg/KVs2wmSfTZ81upJZOtPkJsVkgfsgtxxqmAeapjT/vLKfmYV0bS8n5MNVP+g==",
"path": "microsoft.codeanalysis.csharp.workspaces/4.5.0",
"hashPath": "microsoft.codeanalysis.csharp.workspaces.4.5.0.nupkg.sha512"
},
"Microsoft.CodeAnalysis.Workspaces.Common/4.5.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-l4dDRmGELXG72XZaonnOeORyD/T5RpEu5LGHOUIhnv+MmUWDY/m1kWXGwtcgQ5CJ5ynkFiRnIYzTKXYjUs7rbw==",
"path": "microsoft.codeanalysis.workspaces.common/4.5.0",
"hashPath": "microsoft.codeanalysis.workspaces.common.4.5.0.nupkg.sha512"
},
"Microsoft.EntityFrameworkCore/8.0.8": {
"type": "package",
"serviceable": true,
"sha512": "sha512-iK+jrJzkfbIxutB7or808BPmJtjUEi5O+eSM7cLDwsyde6+3iOujCSfWnrHrLxY3u+EQrJD+aD8DJ6ogPA2Rtw==",
"path": "microsoft.entityframeworkcore/8.0.8",
"hashPath": "microsoft.entityframeworkcore.8.0.8.nupkg.sha512"
},
"Microsoft.EntityFrameworkCore.Abstractions/8.0.8": {
"type": "package",
"serviceable": true,
"sha512": "sha512-9mMQkZsfL1c2iifBD8MWRmwy59rvsVtR9NOezJj7+g1j4P7g49MJHd8k8faC/v7d5KuHkQ6KOQiSItvoRt9PXA==",
"path": "microsoft.entityframeworkcore.abstractions/8.0.8",
"hashPath": "microsoft.entityframeworkcore.abstractions.8.0.8.nupkg.sha512"
},
"Microsoft.EntityFrameworkCore.Analyzers/8.0.8": {
"type": "package",
"serviceable": true,
"sha512": "sha512-OlAXMU+VQgLz5y5/SBkLvAa9VeiR3dlJqgIebEEH2M2NGA3evm68/Tv7SLWmSxwnEAtA3nmDEZF2pacK6eXh4Q==",
"path": "microsoft.entityframeworkcore.analyzers/8.0.8",
"hashPath": "microsoft.entityframeworkcore.analyzers.8.0.8.nupkg.sha512"
},
"Microsoft.EntityFrameworkCore.Design/8.0.8": {
"type": "package",
"serviceable": true,
"sha512": "sha512-MmQAMHdjZR8Iyn/FVQrh9weJQTn0HqtKa3vELS9ffQJat/qXgnTam9M9jqvePphjkYp5Scee+Hy+EJR4nmWmOA==",
"path": "microsoft.entityframeworkcore.design/8.0.8",
"hashPath": "microsoft.entityframeworkcore.design.8.0.8.nupkg.sha512"
},
"Microsoft.EntityFrameworkCore.Relational/8.0.8": {
"type": "package",
"serviceable": true,
"sha512": "sha512-3WnrwdXxKg4L98cDx0lNEEau8U2lsfuBJCs0Yzht+5XVTmahboM7MukKfQHAzVsHUPszm6ci929S7Qas0WfVHA==",
"path": "microsoft.entityframeworkcore.relational/8.0.8",
"hashPath": "microsoft.entityframeworkcore.relational.8.0.8.nupkg.sha512"
},
"Microsoft.EntityFrameworkCore.Tools/8.0.8": {
"type": "package",
"serviceable": true,
"sha512": "sha512-wjDNbLJk86QpZt2JxJuNVzpBKIbEQsgcJYHGeIFTBuK6NEgvJvyxgneg059HfSJmTVdInZ61lTO4sJGCfFr7+w==",
"path": "microsoft.entityframeworkcore.tools/8.0.8",
"hashPath": "microsoft.entityframeworkcore.tools.8.0.8.nupkg.sha512"
},
"Microsoft.Extensions.Caching.Abstractions/8.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-3KuSxeHoNYdxVYfg2IRZCThcrlJ1XJqIXkAWikCsbm5C/bCjv7G0WoKDyuR98Q+T607QT2Zl5GsbGRkENcV2yQ==",
"path": "microsoft.extensions.caching.abstractions/8.0.0",
"hashPath": "microsoft.extensions.caching.abstractions.8.0.0.nupkg.sha512"
},
"Microsoft.Extensions.Caching.Memory/8.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-7pqivmrZDzo1ADPkRwjy+8jtRKWRCPag9qPI+p7sgu7Q4QreWhcvbiWXsbhP+yY8XSiDvZpu2/LWdBv7PnmOpQ==",
"path": "microsoft.extensions.caching.memory/8.0.0",
"hashPath": "microsoft.extensions.caching.memory.8.0.0.nupkg.sha512"
},
"Microsoft.Extensions.Configuration.Abstractions/8.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-3lE/iLSutpgX1CC0NOW70FJoGARRHbyKmG7dc0klnUZ9Dd9hS6N/POPWhKhMLCEuNN5nXEY5agmlFtH562vqhQ==",
"path": "microsoft.extensions.configuration.abstractions/8.0.0",
"hashPath": "microsoft.extensions.configuration.abstractions.8.0.0.nupkg.sha512"
},
"Microsoft.Extensions.DependencyInjection/8.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-V8S3bsm50ig6JSyrbcJJ8bW2b9QLGouz+G1miK3UTaOWmMtFwNNNzUf4AleyDWUmTrWMLNnFSLEQtxmxgNQnNQ==",
"path": "microsoft.extensions.dependencyinjection/8.0.0",
"hashPath": "microsoft.extensions.dependencyinjection.8.0.0.nupkg.sha512"
},
"Microsoft.Extensions.DependencyInjection.Abstractions/8.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-cjWrLkJXK0rs4zofsK4bSdg+jhDLTaxrkXu4gS6Y7MAlCvRyNNgwY/lJi5RDlQOnSZweHqoyvgvbdvQsRIW+hg==",
"path": "microsoft.extensions.dependencyinjection.abstractions/8.0.0",
"hashPath": "microsoft.extensions.dependencyinjection.abstractions.8.0.0.nupkg.sha512"
},
"Microsoft.Extensions.DependencyModel/8.0.1": {
"type": "package",
"serviceable": true,
"sha512": "sha512-5Ou6varcxLBzQ+Agfm0k0pnH7vrEITYlXMDuE6s7ZHlZHz6/G8XJ3iISZDr5rfwfge6RnXJ1+Wc479mMn52vjA==",
"path": "microsoft.extensions.dependencymodel/8.0.1",
"hashPath": "microsoft.extensions.dependencymodel.8.0.1.nupkg.sha512"
},
"Microsoft.Extensions.Identity.Core/8.0.8": {
"type": "package",
"serviceable": true,
"sha512": "sha512-ICzYDXrPFaJWX2c/fp+AvltzVu2LZIe5eK5+JbxndTjGqYEzAAW9jwKSaBq9c3QF0wM9c3SjI80E2Kcn+l9prg==",
"path": "microsoft.extensions.identity.core/8.0.8",
"hashPath": "microsoft.extensions.identity.core.8.0.8.nupkg.sha512"
},
"Microsoft.Extensions.Identity.Stores/8.0.8": {
"type": "package",
"serviceable": true,
"sha512": "sha512-Lmv3ZQMmanFVacoGChUIP+PhyIsDPFvhbiMX4o/BwJtViZw2ofHXn0CwTlytkBk1zW6S39rGpkfrtAWIUzzLZA==",
"path": "microsoft.extensions.identity.stores/8.0.8",
"hashPath": "microsoft.extensions.identity.stores.8.0.8.nupkg.sha512"
},
"Microsoft.Extensions.Logging/8.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-tvRkov9tAJ3xP51LCv3FJ2zINmv1P8Hi8lhhtcKGqM+ImiTCC84uOPEI4z8Cdq2C3o9e+Aa0Gw0rmrsJD77W+w==",
"path": "microsoft.extensions.logging/8.0.0",
"hashPath": "microsoft.extensions.logging.8.0.0.nupkg.sha512"
},
"Microsoft.Extensions.Logging.Abstractions/8.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-arDBqTgFCyS0EvRV7O3MZturChstm50OJ0y9bDJvAcmEPJm0FFpFyjU/JLYyStNGGey081DvnQYlncNX5SJJGA==",
"path": "microsoft.extensions.logging.abstractions/8.0.0",
"hashPath": "microsoft.extensions.logging.abstractions.8.0.0.nupkg.sha512"
},
"Microsoft.Extensions.Options/8.0.2": {
"type": "package",
"serviceable": true,
"sha512": "sha512-dWGKvhFybsaZpGmzkGCbNNwBD1rVlWzrZKANLW/CcbFJpCEceMCGzT7zZwHOGBCbwM0SzBuceMj5HN1LKV1QqA==",
"path": "microsoft.extensions.options/8.0.2",
"hashPath": "microsoft.extensions.options.8.0.2.nupkg.sha512"
},
"Microsoft.Extensions.Primitives/8.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-bXJEZrW9ny8vjMF1JV253WeLhpEVzFo1lyaZu1vQ4ZxWUlVvknZ/+ftFgVheLubb4eZPSwwxBeqS1JkCOjxd8g==",
"path": "microsoft.extensions.primitives/8.0.0",
"hashPath": "microsoft.extensions.primitives.8.0.0.nupkg.sha512"
},
"Mono.TextTemplating/2.2.1": {
"type": "package",
"serviceable": true,
"sha512": "sha512-KZYeKBET/2Z0gY1WlTAK7+RHTl7GSbtvTLDXEZZojUdAPqpQNDL6tHv7VUpqfX5VEOh+uRGKaZXkuD253nEOBQ==",
"path": "mono.texttemplating/2.2.1",
"hashPath": "mono.texttemplating.2.2.1.nupkg.sha512"
},
"MySqlConnector/2.3.7": {
"type": "package",
"serviceable": true,
"sha512": "sha512-YiVOxvJ+vAYW8NT9gHv8RxKCDFCSXAObF3z0Ou/8WRv8Lsn2QsxaPW5xEwPE+xCcAq6BGkrI8GTOC09Xg09blQ==",
"path": "mysqlconnector/2.3.7",
"hashPath": "mysqlconnector.2.3.7.nupkg.sha512"
},
"Pomelo.EntityFrameworkCore.MySql/8.0.2": {
"type": "package",
"serviceable": true,
"sha512": "sha512-XjnlcxVBLnEMbyEc5cZzgZeDyLvAniACZQ04W1slWN0f4rmfNzl98gEMvHnFH0fMDF06z9MmgGi/Sr7hJ+BVnw==",
"path": "pomelo.entityframeworkcore.mysql/8.0.2",
"hashPath": "pomelo.entityframeworkcore.mysql.8.0.2.nupkg.sha512"
},
"System.CodeDom/4.4.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-2sCCb7doXEwtYAbqzbF/8UAeDRMNmPaQbU2q50Psg1J9KzumyVVCgKQY8s53WIPTufNT0DpSe9QRvVjOzfDWBA==",
"path": "system.codedom/4.4.0",
"hashPath": "system.codedom.4.4.0.nupkg.sha512"
},
"System.Collections.Immutable/6.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-l4zZJ1WU2hqpQQHXz1rvC3etVZN+2DLmQMO79FhOTZHMn8tDRr+WU287sbomD0BETlmKDn0ygUgVy9k5xkkJdA==",
"path": "system.collections.immutable/6.0.0",
"hashPath": "system.collections.immutable.6.0.0.nupkg.sha512"
},
"System.Composition/6.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-d7wMuKQtfsxUa7S13tITC8n1cQzewuhD5iDjZtK2prwFfKVzdYtgrTHgjaV03Zq7feGQ5gkP85tJJntXwInsJA==",
"path": "system.composition/6.0.0",
"hashPath": "system.composition.6.0.0.nupkg.sha512"
},
"System.Composition.AttributedModel/6.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-WK1nSDLByK/4VoC7fkNiFuTVEiperuCN/Hyn+VN30R+W2ijO1d0Z2Qm0ScEl9xkSn1G2MyapJi8xpf4R8WRa/w==",
"path": "system.composition.attributedmodel/6.0.0",
"hashPath": "system.composition.attributedmodel.6.0.0.nupkg.sha512"
},
"System.Composition.Convention/6.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-XYi4lPRdu5bM4JVJ3/UIHAiG6V6lWWUlkhB9ab4IOq0FrRsp0F4wTyV4Dj+Ds+efoXJ3qbLqlvaUozDO7OLeXA==",
"path": "system.composition.convention/6.0.0",
"hashPath": "system.composition.convention.6.0.0.nupkg.sha512"
},
"System.Composition.Hosting/6.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-w/wXjj7kvxuHPLdzZ0PAUt++qJl03t7lENmb2Oev0n3zbxyNULbWBlnd5J5WUMMv15kg5o+/TCZFb6lSwfaUUQ==",
"path": "system.composition.hosting/6.0.0",
"hashPath": "system.composition.hosting.6.0.0.nupkg.sha512"
},
"System.Composition.Runtime/6.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-qkRH/YBaMPTnzxrS5RDk1juvqed4A6HOD/CwRcDGyPpYps1J27waBddiiq1y93jk2ZZ9wuA/kynM+NO0kb3PKg==",
"path": "system.composition.runtime/6.0.0",
"hashPath": "system.composition.runtime.6.0.0.nupkg.sha512"
},
"System.Composition.TypedParts/6.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-iUR1eHrL8Cwd82neQCJ00MpwNIBs4NZgXzrPqx8NJf/k4+mwBO0XCRmHYJT4OLSwDDqh5nBLJWkz5cROnrGhRA==",
"path": "system.composition.typedparts/6.0.0",
"hashPath": "system.composition.typedparts.6.0.0.nupkg.sha512"
},
"System.IO.Pipelines/6.0.3": {
"type": "package",
"serviceable": true,
"sha512": "sha512-ryTgF+iFkpGZY1vRQhfCzX0xTdlV3pyaTTqRu2ETbEv+HlV7O6y7hyQURnghNIXvctl5DuZ//Dpks6HdL/Txgw==",
"path": "system.io.pipelines/6.0.3",
"hashPath": "system.io.pipelines.6.0.3.nupkg.sha512"
},
"System.Reflection.Metadata/6.0.1": {
"type": "package",
"serviceable": true,
"sha512": "sha512-III/lNMSn0ZRBuM9m5Cgbiho5j81u0FAEagFX5ta2DKbljZ3T0IpD8j+BIiHQPeKqJppWS9bGEp6JnKnWKze0g==",
"path": "system.reflection.metadata/6.0.1",
"hashPath": "system.reflection.metadata.6.0.1.nupkg.sha512"
},
"System.Runtime.CompilerServices.Unsafe/6.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==",
"path": "system.runtime.compilerservices.unsafe/6.0.0",
"hashPath": "system.runtime.compilerservices.unsafe.6.0.0.nupkg.sha512"
},
"System.Text.Encoding.CodePages/6.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-ZFCILZuOvtKPauZ/j/swhvw68ZRi9ATCfvGbk1QfydmcXBkIWecWKn/250UH7rahZ5OoDBaiAudJtPvLwzw85A==",
"path": "system.text.encoding.codepages/6.0.0",
"hashPath": "system.text.encoding.codepages.6.0.0.nupkg.sha512"
},
"System.Text.Encodings.Web/8.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-yev/k9GHAEGx2Rg3/tU6MQh4HGBXJs70y7j1LaM1i/ER9po+6nnQ6RRqTJn1E7Xu0fbIFK80Nh5EoODxrbxwBQ==",
"path": "system.text.encodings.web/8.0.0",
"hashPath": "system.text.encodings.web.8.0.0.nupkg.sha512"
},
"System.Text.Json/8.0.4": {
"type": "package",
"serviceable": true,
"sha512": "sha512-bAkhgDJ88XTsqczoxEMliSrpijKZHhbJQldhAmObj/RbrN3sU5dcokuXmWJWsdQAhiMJ9bTayWsL1C9fbbCRhw==",
"path": "system.text.json/8.0.4",
"hashPath": "system.text.json.8.0.4.nupkg.sha512"
},
"System.Threading.Channels/6.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-TY8/9+tI0mNaUMgntOxxaq2ndTkdXqLSxvPmas7XEqOlv9lQtB7wLjYGd756lOaO7Dvb5r/WXhluM+0Xe87v5Q==",
"path": "system.threading.channels/6.0.0",
"hashPath": "system.threading.channels.6.0.0.nupkg.sha512"
}
}
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,20 @@
{
"runtimeOptions": {
"tfm": "net8.0",
"frameworks": [
{
"name": "Microsoft.NETCore.App",
"version": "8.0.0"
},
{
"name": "Microsoft.AspNetCore.App",
"version": "8.0.0"
}
],
"configProperties": {
"System.GC.Server": true,
"System.Reflection.NullabilityInfoContext.IsSupported": true,
"System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization": false
}
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

Binary file not shown.

Binary file not shown.

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More