diff --git a/web/wwwroot/css/myai.css b/web/wwwroot/css/myai.css index 14ba18c..bf4a8d1 100644 --- a/web/wwwroot/css/myai.css +++ b/web/wwwroot/css/myai.css @@ -1 +1,735 @@ -:root{--bg:#041120;--bg-soft:#0a1c34;--panel:rgba(10,22,42,.82);--panel-border:rgba(255,255,255,.1);--text:#eaf1ff;--muted:#9bb0d0;--primary:#5fa0ff;--primary-strong:#8b6cff;--card-radius:28px;--shadow:0 18px 60px rgba(0,0,0,.28)}*{box-sizing:border-box}html{scroll-behavior:smooth}body{margin:0;font-family:Inter,sans-serif;background:radial-gradient(circle at top left,#12345d 0%,#071326 35%,#030915 100%);color:var(--text)}a{color:inherit;text-decoration:none}img{max-width:100%;display:block}.container{width:100%;max-width:1120px;margin:0 auto;padding-left:20px;padding-right:20px}.site-shell{overflow:hidden}.header{position:sticky;top:0;z-index:20;background:rgba(3,11,23,.76);backdrop-filter:blur(16px);border-bottom:1px solid rgba(255,255,255,.08)}.nav-wrap{display:flex;align-items:center;justify-content:space-between;gap:20px;min-height:84px}.brand{display:flex;align-items:center;gap:14px}.brand-mark{width:48px;height:48px}.ai-mark{display:inline-flex;align-items:center;justify-content:center;border-radius:16px;background:linear-gradient(135deg,var(--primary),var(--primary-strong));font-weight:900;color:#fff;box-shadow:0 18px 40px rgba(95,160,255,.24)}.brand-text{display:block;font-size:1.7rem;font-weight:800}.brand small{display:block;color:var(--muted);margin-top:2px}.nav{display:flex;align-items:center;gap:32px}.nav a{color:#d7e3fb;font-weight:600}.nav a:hover{color:#fff}.menu-toggle{display:none;background:transparent;border:0;padding:8px}.menu-toggle span{display:block;width:24px;height:2px;background:#fff;margin:5px 0}.hero{padding:72px 0 48px}.hero-grid{display:grid;grid-template-columns:1.05fr .95fr;gap:42px;align-items:center}.eyebrow{display:inline-flex;align-items:center;gap:8px;margin-bottom:14px;color:#8fb8ff;text-transform:uppercase;letter-spacing:.18em;font-size:.78rem;font-weight:800}.hero h1{font-size:clamp(2.3rem,5vw,4.8rem);line-height:1.02;margin:0 0 24px;letter-spacing:-.05em}.hero-text{font-size:1.12rem;line-height:1.8;color:var(--muted);max-width:650px}.hero-actions{display:flex;gap:14px;flex-wrap:wrap;margin-top:30px}.btn{border-radius:999px;padding:13px 22px;font-weight:800;border:1px solid rgba(255,255,255,.12)}.btn-primary{background:linear-gradient(135deg,var(--primary),var(--primary-strong));border:0;color:#fff}.btn-secondary{background:rgba(255,255,255,.06);color:#fff}.section{padding:76px 0}.section-heading{max-width:720px;margin-bottom:34px}.section-heading h2,.contact h2,.ai-panel h2{font-size:clamp(2rem,4vw,3.2rem);line-height:1.05;letter-spacing:-.04em;margin:0 0 18px}.section-heading p,.contact p{color:var(--muted);font-size:1.05rem;line-height:1.8}.ai-console-card,.ai-panel,.demo-card,.contact-form{background:var(--panel);border:1px solid var(--panel-border);border-radius:var(--card-radius);box-shadow:var(--shadow)}.ai-console-card{padding:30px}.console-line{display:flex;gap:16px;align-items:center;margin:12px 0;padding:16px 18px;border-radius:18px;background:rgba(255,255,255,.05);color:#dce8ff}.console-line span{min-width:76px;color:#8fb8ff;font-weight:900}.demo-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:20px}.demo-card{display:block;padding:26px;min-height:260px;transition:transform .2s ease,border-color .2s ease}.demo-card:hover{transform:translateY(-4px);border-color:rgba(95,160,255,.45)}.demo-card h3{font-size:1.55rem;margin:18px 0 12px}.demo-card p{color:var(--muted);line-height:1.7}.muted-card{opacity:.7}.product-tag{display:inline-flex;border-radius:999px;padding:7px 12px;background:rgba(95,160,255,.12);color:#b9d4ff;border:1px solid rgba(95,160,255,.18);font-weight:800;font-size:.82rem}.matcher-grid{display:grid;grid-template-columns:1fr 1fr;gap:24px;align-items:start}.ai-panel{padding:28px}.ai-panel label,.contact-form label{display:block;margin-bottom:18px}.ai-panel label span,.contact-form label span{display:block;margin-bottom:8px;color:#c3d4f2;font-weight:700}.ai-panel input,.ai-panel textarea,.contact-form input,.contact-form textarea{width:100%;border:1px solid rgba(255,255,255,.12);border-radius:18px;background:rgba(0,0,0,.25);color:#fff;padding:15px 16px;outline:none}.ai-panel input:focus,.ai-panel textarea:focus,.contact-form input:focus,.contact-form textarea:focus{border-color:rgba(95,160,255,.65)}.file-drop{display:block;border:1px dashed rgba(143,184,255,.45);border-radius:22px;background:rgba(95,160,255,.07);padding:22px;cursor:pointer}.file-drop input{display:none}.file-drop strong{display:block;font-size:1.1rem}.file-drop span{color:var(--muted)!important;margin-top:8px}.consent-inline{display:flex;gap:12px;align-items:flex-start;margin:18px 0 22px;color:#b7c7e4;line-height:1.6}.consent-inline input{width:auto;margin-top:5px}.consent-inline label{margin:0}.result-panel{position:sticky;top:110px}.empty-result{color:var(--muted);line-height:1.8;padding:20px;border-radius:18px;background:rgba(0,0,0,.25)}.score-badge{display:inline-flex;align-items:center;justify-content:center;width:104px;height:104px;border-radius:50%;background:linear-gradient(135deg,var(--primary),var(--primary-strong));font-size:2rem;font-weight:900;margin:10px 0 20px}.result-list{padding-left:18px;color:#d7e3fb;line-height:1.8}.contact{background:rgba(255,255,255,.03)}.contact-grid{display:grid;grid-template-columns:.9fr 1.1fr;gap:32px;align-items:start}.contact-list{display:grid;gap:14px;margin-top:24px}.contact-list div{padding:18px;border-radius:20px;background:rgba(255,255,255,.05);border:1px solid rgba(255,255,255,.08)}.contact-list span{display:block;color:var(--muted);font-size:.88rem}.contact-form{padding:28px}.form-message{display:block;margin-top:14px}.text-success{color:#7ef2a7!important}.text-danger{color:#ff8a8a!important}.footer{padding:26px 0;border-top:1px solid rgba(255,255,255,.08);background:rgba(0,0,0,.18)}.footer-wrap{display:flex;align-items:center;justify-content:space-between;gap:20px;flex-wrap:wrap;color:var(--muted)}.footer-links{display:flex;gap:18px;flex-wrap:wrap}.cookie-overlay{position:fixed;left:0;right:0;bottom:20px;z-index:50;padding:0 20px}.cookie-box{max-width:980px;margin:auto;padding:20px;border-radius:24px;background:#071326;border:1px solid rgba(255,255,255,.16);box-shadow:var(--shadow);display:flex;align-items:center;justify-content:space-between;gap:20px}.cookie-text{color:#dce8ff;line-height:1.6}.cookie-text a{color:#9cc5ff;text-decoration:underline}.cookie-actions{display:flex;gap:10px;flex-wrap:wrap}.cookie-manage{position:fixed;right:20px;bottom:20px;z-index:40}.loader-overlay{position:fixed;inset:0;z-index:80;background:rgba(0,0,0,.55);align-items:center;justify-content:center}.loader-box{padding:20px 30px;border-radius:18px;background:#071326;border:1px solid rgba(255,255,255,.16);font-weight:800}.shake{animation:shake .35s}@keyframes shake{25%{transform:translateX(-5px)}50%{transform:translateX(5px)}75%{transform:translateX(-3px)}}@media (max-width:900px){.hero-grid,.matcher-grid,.contact-grid,.demo-grid{grid-template-columns:1fr}.result-panel{position:static}.nav{position:absolute;top:84px;left:20px;right:20px;display:none;flex-direction:column;align-items:flex-start;background:#071326;border:1px solid rgba(255,255,255,.12);border-radius:20px;padding:20px}.nav.is-open{display:flex}.menu-toggle{display:block}.cookie-box{align-items:flex-start;flex-direction:column}}@media (max-width:560px){.hero{padding-top:46px}.section{padding:56px 0}.footer-wrap{align-items:flex-start;flex-direction:column}.hero-actions .btn{width:100%;text-align:center}} +:root { + --bg: #041120; + --bg-soft: #0a1c34; + --panel: rgba(10,22,42,.82); + --panel-border: rgba(255,255,255,.1); + --text: #eaf1ff; + --muted: #9bb0d0; + --primary: #5fa0ff; + --primary-strong: #8b6cff; + --card-radius: 28px; + --shadow: 0 18px 60px rgba(0,0,0,.28) +} + +* { + box-sizing: border-box +} + +html { + scroll-behavior: smooth +} + +body { + margin: 0; + font-family: Inter,sans-serif; + background: radial-gradient(circle at top left,#12345d 0%,#071326 35%,#030915 100%); + color: var(--text) +} + +a { + color: inherit; + text-decoration: none +} + +img { + max-width: 100%; + display: block +} + +.container { + width: 100%; + max-width: 1120px; + margin: 0 auto; + padding-left: 20px; + padding-right: 20px +} + +.site-shell { + overflow: hidden +} + +.header { + position: sticky; + top: 0; + z-index: 20; + background: rgba(3,11,23,.76); + backdrop-filter: blur(16px); + border-bottom: 1px solid rgba(255,255,255,.08) +} + +.nav-wrap { + display: flex; + align-items: center; + justify-content: space-between; + gap: 20px; + min-height: 84px +} + +.brand { + display: flex; + align-items: center; + gap: 14px +} + +.brand-mark { + width: 48px; + height: 48px +} + +.ai-mark { + display: inline-flex; + align-items: center; + justify-content: center; + border-radius: 16px; + background: linear-gradient(135deg,var(--primary),var(--primary-strong)); + font-weight: 900; + color: #fff; + box-shadow: 0 18px 40px rgba(95,160,255,.24) +} + +.brand-text { + display: block; + font-size: 1.7rem; + font-weight: 800 +} + +.brand small { + display: block; + color: var(--muted); + margin-top: 2px +} + +.nav { + display: flex; + align-items: center; + gap: 32px +} + + .nav a { + color: #d7e3fb; + font-weight: 600 + } + + .nav a:hover { + color: #fff + } + +.menu-toggle { + display: none; + background: transparent; + border: 0; + padding: 8px +} + + .menu-toggle span { + display: block; + width: 24px; + height: 2px; + background: #fff; + margin: 5px 0 + } + +.hero { + padding: 72px 0 48px +} + +.hero-grid { + display: grid; + grid-template-columns: 1.05fr .95fr; + gap: 42px; + align-items: center +} + +.eyebrow { + display: inline-flex; + align-items: center; + gap: 8px; + margin-bottom: 14px; + color: #8fb8ff; + text-transform: uppercase; + letter-spacing: .18em; + font-size: .78rem; + font-weight: 800 +} + +.hero h1 { + font-size: clamp(2.3rem,5vw,4.8rem); + line-height: 1.02; + margin: 0 0 24px; + letter-spacing: -.05em +} + +.hero-text { + font-size: 1.12rem; + line-height: 1.8; + color: var(--muted); + max-width: 650px +} + +.hero-actions { + display: flex; + gap: 14px; + flex-wrap: wrap; + margin-top: 30px +} + +.btn { + border-radius: 999px; + padding: 13px 22px; + font-weight: 800; + border: 1px solid rgba(255,255,255,.12) +} + +.btn-primary { + background: linear-gradient(135deg,var(--primary),var(--primary-strong)); + border: 0; + color: #fff +} + +.btn-secondary { + background: rgba(255,255,255,.06); + color: #fff +} + +.section { + padding: 76px 0 +} + +.section-heading { + max-width: 720px; + margin-bottom: 34px +} + + .section-heading h2, .contact h2, .ai-panel h2 { + font-size: clamp(2rem,4vw,3.2rem); + line-height: 1.05; + letter-spacing: -.04em; + margin: 0 0 18px + } + + .section-heading p, .contact p { + color: var(--muted); + font-size: 1.05rem; + line-height: 1.8 + } + +.ai-console-card, .ai-panel, .demo-card, .contact-form { + background: var(--panel); + border: 1px solid var(--panel-border); + border-radius: var(--card-radius); + box-shadow: var(--shadow) +} + +.ai-console-card { + padding: 30px +} + +.console-line { + display: flex; + gap: 16px; + align-items: center; + margin: 12px 0; + padding: 16px 18px; + border-radius: 18px; + background: rgba(255,255,255,.05); + color: #dce8ff +} + + .console-line span { + min-width: 76px; + color: #8fb8ff; + font-weight: 900 + } + +.demo-grid { + display: grid; + grid-template-columns: repeat(3,1fr); + gap: 20px +} + +.demo-card { + display: block; + padding: 26px; + min-height: 260px; + transition: transform .2s ease,border-color .2s ease +} + + .demo-card:hover { + transform: translateY(-4px); + border-color: rgba(95,160,255,.45) + } + + .demo-card h3 { + font-size: 1.55rem; + margin: 18px 0 12px + } + + .demo-card p { + color: var(--muted); + line-height: 1.7 + } + +.muted-card { + opacity: .7 +} + +.product-tag { + display: inline-flex; + border-radius: 999px; + padding: 7px 12px; + background: rgba(95,160,255,.12); + color: #b9d4ff; + border: 1px solid rgba(95,160,255,.18); + font-weight: 800; + font-size: .82rem +} + +.matcher-grid { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 24px; + align-items: start +} + +.ai-panel { + padding: 28px +} + + .ai-panel label, .contact-form label { + display: block; + margin-bottom: 18px + } + + .ai-panel label span, .contact-form label span { + display: block; + margin-bottom: 8px; + color: #c3d4f2; + font-weight: 700 + } + + .ai-panel input, .ai-panel textarea, .contact-form input, .contact-form textarea { + width: 100%; + border: 1px solid rgba(255,255,255,.12); + border-radius: 18px; + background: rgba(0,0,0,.25); + color: #fff; + padding: 15px 16px; + outline: none + } + + .ai-panel input:focus, .ai-panel textarea:focus, .contact-form input:focus, .contact-form textarea:focus { + border-color: rgba(95,160,255,.65) + } + +.file-drop { + display: block; + border: 1px dashed rgba(143,184,255,.45); + border-radius: 22px; + background: rgba(95,160,255,.07); + padding: 22px; + cursor: pointer +} + + .file-drop input { + display: none + } + + .file-drop strong { + display: block; + font-size: 1.1rem + } + + .file-drop span { + color: var(--muted) !important; + margin-top: 8px + } + +.consent-inline { + display: flex; + gap: 12px; + align-items: flex-start; + margin: 18px 0 22px; + color: #b7c7e4; + line-height: 1.6 +} + + .consent-inline input { + width: auto; + margin-top: 5px + } + + .consent-inline label { + margin: 0 + } + +.result-panel { + position: sticky; + top: 110px +} + +.empty-result { + color: var(--muted); + line-height: 1.8; + padding: 20px; + border-radius: 18px; + background: rgba(0,0,0,.25) +} + +.score-badge { + display: inline-flex; + align-items: center; + justify-content: center; + width: 104px; + height: 104px; + border-radius: 50%; + background: linear-gradient(135deg,var(--primary),var(--primary-strong)); + font-size: 2rem; + font-weight: 900; + margin: 10px 0 20px +} + +.result-list { + padding-left: 18px; + color: #d7e3fb; + line-height: 1.8 +} + +.contact { + background: rgba(255,255,255,.03) +} + +.contact-grid { + display: grid; + grid-template-columns: .9fr 1.1fr; + gap: 32px; + align-items: start +} + +.contact-list { + display: grid; + gap: 14px; + margin-top: 24px +} + + .contact-list div { + padding: 18px; + border-radius: 20px; + background: rgba(255,255,255,.05); + border: 1px solid rgba(255,255,255,.08) + } + + .contact-list span { + display: block; + color: var(--muted); + font-size: .88rem + } + +.contact-form { + padding: 28px +} + +.form-message { + display: block; + margin-top: 14px +} + +.text-success { + color: #7ef2a7 !important +} + +.text-danger { + color: #ff8a8a !important +} + +.footer { + padding: 26px 0; + border-top: 1px solid rgba(255,255,255,.08); + background: rgba(0,0,0,.18) +} + +.footer-wrap { + display: flex; + align-items: center; + justify-content: space-between; + gap: 20px; + flex-wrap: wrap; + color: var(--muted) +} + +.footer-links { + display: flex; + gap: 18px; + flex-wrap: wrap +} + +.cookie-overlay { + position: fixed; + left: 0; + right: 0; + bottom: 20px; + z-index: 50; + padding: 0 20px +} + +.cookie-box { + max-width: 980px; + margin: auto; + padding: 20px; + border-radius: 24px; + background: #071326; + border: 1px solid rgba(255,255,255,.16); + box-shadow: var(--shadow); + display: flex; + align-items: center; + justify-content: space-between; + gap: 20px +} + +.cookie-text { + color: #dce8ff; + line-height: 1.6 +} + + .cookie-text a { + color: #9cc5ff; + text-decoration: underline + } + +.cookie-actions { + display: flex; + gap: 10px; + flex-wrap: wrap +} + +.cookie-manage { + position: fixed; + bottom: 20px; + z-index: 40 +} + +.loader-overlay { + position: fixed; + inset: 0; + z-index: 80; + background: rgba(0,0,0,.55); + align-items: center; + justify-content: center +} + +.loader-box { + padding: 20px 30px; + border-radius: 18px; + background: #071326; + border: 1px solid rgba(255,255,255,.16); + font-weight: 800 +} + +.shake { + animation: shake .35s +} + +@keyframes shake { + 25% { + transform: translateX(-5px) + } + + 50% { + transform: translateX(5px) + } + + 75% { + transform: translateX(-3px) + } +} + +@media (max-width:900px) { + .hero-grid, .matcher-grid, .contact-grid, .demo-grid { + grid-template-columns: 1fr + } + + .result-panel { + position: static + } + + .nav { + position: absolute; + top: 84px; + left: 20px; + right: 20px; + display: none; + flex-direction: column; + align-items: flex-start; + background: #071326; + border: 1px solid rgba(255,255,255,.12); + border-radius: 20px; + padding: 20px + } + + .nav.is-open { + display: flex + } + + .menu-toggle { + display: block + } + + .cookie-box { + align-items: flex-start; + flex-direction: column + } +} + +@media (max-width:560px) { + .hero { + padding-top: 46px + } + + .section { + padding: 56px 0 + } + + .footer-wrap { + align-items: flex-start; + flex-direction: column + } + + .hero-actions .btn { + width: 100%; + text-align: center + } +} + +/* MyAi brand + main-page language selector */ +.brand-mark img { + width: 100%; + height: 100%; + object-fit: contain; + filter: drop-shadow(0 16px 26px rgba(95,160,255,.24)) +} + +.nav-actions { + display: flex; + align-items: center; + gap: 12px +} + +.lang-switch { + display: flex; + align-items: center; + gap: 8px; + padding: 6px; + border-radius: 18px; + background: rgba(255,255,255,.04); + border: 1px solid rgba(255,255,255,.1) +} + +.lang-flag { + width: 46px; + height: 32px; + padding: 3px; + display: inline-flex; + align-items: center; + justify-content: center; + border: 1px solid transparent; + border-radius: 12px; + background: transparent; + cursor: pointer; + opacity: .78; + transition: transform .2s ease,border-color .2s ease,background-color .2s ease,opacity .2s ease +} + + .lang-flag img { + width: 100%; + height: 100%; + display: block; + border-radius: 8px; + object-fit: cover; + box-shadow: 0 8px 18px rgba(0,0,0,.18) + } + + .lang-flag:hover { + opacity: 1; + transform: translateY(-1px) + } + + .lang-flag[aria-pressed=true] { + opacity: 1; + border-color: rgba(95,160,255,.65); + background: rgba(95,160,255,.1) + } + +.banner-card { + overflow: hidden +} + +.showcase-banner { + width: 100%; + border-radius: 22px; + margin-bottom: 18px; + border: 1px solid rgba(255,255,255,.12); + box-shadow: 0 20px 55px rgba(0,0,0,.22) +} + +.console-line b { + font-weight: 700; + color: #dce8ff +} + +@media (max-width:900px) { + .nav-actions { + margin-left: auto + } + + .nav-wrap { + position: relative + } + + .menu-toggle { + order: 4 + } + + .nav { + z-index: 30 + } + + .lang-switch { + padding: 4px + } + + .lang-flag { + width: 42px; + height: 30px + } +} + +@media (max-width:560px) { + .brand-text { + font-size: 1.35rem + } + + .brand small { + display: none + } + + .brand-mark { + width: 42px; + height: 42px + } + + .nav-actions { + gap: 6px + } + + .lang-switch { + gap: 4px + } + + .lang-flag { + width: 36px; + height: 27px + } + + .showcase-banner { + border-radius: 18px + } +} diff --git a/web/wwwroot/cv-matcher/index.html b/web/wwwroot/cv-matcher/index.html index 809b42c..8c60122 100644 --- a/web/wwwroot/cv-matcher/index.html +++ b/web/wwwroot/cv-matcher/index.html @@ -11,6 +11,8 @@ + + @@ -21,79 +23,177 @@
-
- AI CV Matcher -

Upload your CV, add a job link, and see how well they match.

-

The backend should extract text from the PDF, create RAG context from your CV, retrieve relevant experience for the job, then score strengths, gaps and next actions.

- + AI CV Matcher +

Upload your CV, add a job link, and see how well they match.

+

The backend should extract text from the PDF, create RAG context from your CV, retrieve relevant experience for the job, then score strengths, gaps and next actions.

+
-
-
1 PDF text extraction
-
2 CV chunking + embeddings
-
3 Job URL/description parsing
-
4 Match score + evidence
+
-
- Input -

CV and job details

- - - - - + Input +

CV and job details

+ + + + +
-
-
- Contact -

Want this adapted for your workflow?

-

This form uses the existing template contact API endpoint.

-
Contact personMihes Gelu
+ Contact +

Want this adapted for your workflow?

+

This form uses the existing template contact API endpoint.

+
+
+ Contact person + Mihes Gelu +
+
+ Phone + + +40 722-523-764 + +
+
- - - - + + + +
- - +
- - - - - + + + - + \ No newline at end of file diff --git a/web/wwwroot/favicon.ico b/web/wwwroot/favicon.ico index 7604114..f16a662 100644 Binary files a/web/wwwroot/favicon.ico and b/web/wwwroot/favicon.ico differ diff --git a/web/wwwroot/img/favicon-256.png b/web/wwwroot/img/favicon-256.png new file mode 100644 index 0000000..93d5a57 Binary files /dev/null and b/web/wwwroot/img/favicon-256.png differ diff --git a/web/wwwroot/img/myai-banner.svg b/web/wwwroot/img/myai-banner.svg new file mode 100644 index 0000000..aa9fdc7 --- /dev/null +++ b/web/wwwroot/img/myai-banner.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + MyAi.ro + Applied AI engineering showcase + RAG + diff --git a/web/wwwroot/img/myai-logo.svg b/web/wwwroot/img/myai-logo.svg new file mode 100644 index 0000000..fdffd46 --- /dev/null +++ b/web/wwwroot/img/myai-logo.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/web/wwwroot/index.html b/web/wwwroot/index.html index 2ddd27e..69c178c 100644 --- a/web/wwwroot/index.html +++ b/web/wwwroot/index.html @@ -11,134 +11,189 @@ + + - - + +
-
-
- Navigator -

Select an AI demo

-

Start with the CV Matcher. More demos can be added here without changing the site structure.

+ Navigator +

Select an AI demo

+

Start with the CV Matcher. More demos can be added here without changing the site structure.

- Available -

CV Matcher

-

Upload a CV PDF, provide a job link or description, extract RAG context and generate a match score with strengths and gaps.

- Open demo → + Available +

CV Matcher

+

Upload a CV PDF, provide a job link or description, extract RAG context and generate a match score with strengths and gaps.

+ Open demo →
- Next -

RAG Playground

-

Experiment with chunk size, retrieval count and source context transparency.

+ Next +

RAG Playground

+

Experiment with chunk size, retrieval count and source context transparency.

- Next -

Agent Automation

-

Job discovery, filtering, ranking and notification workflows.

+ Next +

Agent Automation

+

Job discovery, filtering, ranking and notification workflows.

-
- Contact -

Discuss an AI integration or custom software project

-

Use the form and it will submit through the existing contact API endpoint from the template.

+ Contact +

Discuss an AI integration or custom software project

+

Use the form and it will submit through the existing contact API endpoint from the template.

-
Contact personMihes Gelu
- - +
+ Contact person + Mihes Gelu +
+
+ Phone + + +40 722-523-764 + +
+
+ WhatsApp + + +40 744-564-177 + +
- - - - + + + +
-
- - - + - - - - - + + + + - + \ No newline at end of file diff --git a/web/wwwroot/js/myai.js b/web/wwwroot/js/myai.js index c057698..aa79325 100644 --- a/web/wwwroot/js/myai.js +++ b/web/wwwroot/js/myai.js @@ -1,243 +1,499 @@ (function ($) { - "use strict"; + "use strict"; - var reCaptchaSiteKey = null; - var gTagManagerId = null; - var CONSENT_KEY = "myai_cookie_consent"; + var reCaptchaSiteKey = null; + var gTagManagerId = null; + var CONSENT_KEY = "myai_cookie_consent"; + var LANG_KEY = "myai_lang"; - $('#year').text(new Date().getFullYear()); + var i18n = { + en: { + "brand.subtitle": "AI engineering showcase", + "nav.demos": "Demos", + "nav.contact": "Contact", + "nav.navigator": "Navigator", + "nav.matcher": "Matcher", + "home.eyebrow": "Applied AI lab", + "home.title": "Production-minded AI demos, not generic chatbot wrappers.", + "home.text": "MyAi.ro is a technical showcase for practical AI systems: document understanding, retrieval, matching, automation and decision support.", + "home.openCv": "Open CV Matcher", + "home.step1": "CV.pdf", + "home.step2": "skills, projects, experience", + "home.step3": "relevant CV context", + "home.step4": "job match + gaps", + "home.navigator": "Navigator", + "home.selectDemo": "Select an AI demo", + "home.selectText": "Our first steps in AI integrations.", + "tag.available": "Available", + "tag.next": "Next", + "demo.cv.title": "CV Matcher", + "demo.cv.text": "Upload a CV PDF, provide a job link or description, extract RAG context and generate a match score with strengths and gaps.", + "demo.open": "Open demo →", + "demo.rag.title": "RAG Playground", + "demo.rag.text": "Experiment with chunk size, retrieval count and source context transparency.", + "demo.agent.title": "Agent Automation", + "demo.agent.text": "Job discovery, filtering, ranking and notification workflows.", + "contact.title": "Discuss an AI integration or custom software project", + "contact.text": "Fill in the form fields and will contact you.", + "contact.person": "Contact person", + "contact.phone": "Phone", + "form.name": "Name", + "form.namePlaceholder": "Your name", + "form.email": "Email", + "form.emailPlaceholder": "name@company.com", + "form.message": "Message", + "form.messagePlaceholder": "Tell me what you want to build.", + "form.send": "Send message", + "form.thanks": "Thank you for your message.", + "form.captchaFailed": "Captcha verification failed.", + "form.failed": "Failed to send the message.", + "footer.rights": "All rights reserved", + "footer.top": "Back to top", + "legal.terms": "Terms", + "legal.privacy": "Privacy", + "legal.cookies": "Cookies", + "status.sending": "Sending...", + "cookies.title": "Cookies", + "cookies.text": "We use necessary cookies and, with your consent, analytics through Google Tag Manager.", + "cookies.policy": "Privacy policy", + "cookies.reject": "Reject", + "cookies.necessary": "Necessary only", + "cookies.accept": "Accept analytics", + "cookies.settings": "Cookie settings", + "cv.brand": "CV Matcher", + "cv.eyebrow": "AI CV Matcher", + "cv.title": "Upload your CV, add a job link, and see how well they match.", + "cv.text": "The backend should extract text from the PDF, create RAG context from your CV, retrieve relevant experience for the job, then score strengths, gaps and next actions.", + "cv.try": "Try matcher", + "cv.back": "Back to navigator", + "cv.step1": "PDF text extraction", + "cv.step2": "CV chunking + embeddings", + "cv.step3": "Job URL/description parsing", + "cv.step4": "Match score + evidence", + "cv.input": "Input", + "cv.details": "CV and job details", + "cv.upload": "Upload CV PDF", + "cv.fileHint": "PDF only, max size handled by backend", + "cv.jobLink": "Job link", + "cv.jobDescription": "Or paste job description", + "cv.jobPlaceholder": "Paste the job description if the page cannot be crawled.", + "cv.gdpr": "I agree that my CV is processed for this matching demo. Do not upload sensitive documents unless you trust the deployment.", + "cv.submit": "Extract CV and match job", + "cv.result": "Result", + "cv.analysis": "Match analysis", + "cv.empty": "Upload a CV and provide a job link or description to generate a result.", + "cv.contactTitle": "Want this adapted for your workflow?", + "cv.contactText": "This form uses the existing template contact API endpoint.", + "cv.noFile": "Please upload a CV PDF.", + "cv.noJob": "Add a job link or paste a job description.", + "cv.noConsent": "GDPR consent is required.", + "cv.processing": "Processing...", + "cv.extracting": "Extracting CV and matching job...", + "cv.processingLong": "Processing CV PDF and job input. Backend endpoints must be available.", + "cv.cvFailed": "CV extraction failed", + "cv.matchFailed": "Job matching failed", + "cv.completed": "Match completed.", + "cv.backendMissing": "The frontend is ready, but the backend endpoints /api/rag/cv and /api/rag/match-job must be implemented.", + "cv.noSummary": "No summary returned.", + "cv.noItems": "No items returned.", + "cv.strengths": "Strengths", + "cv.gaps": "Gaps", + "cv.evidence": "Retrieved CV evidence" + }, + ro: { + "brand.subtitle": "prezentare inginerie AI", + "nav.demos": "Demo-uri", + "nav.contact": "Contact", + "nav.navigator": "Navigator", + "nav.matcher": "Matcher", + "home.eyebrow": "Laborator AI aplicat", + "home.title": "Demo-uri AI orientate spre producție, nu simple wrapper-e de chatbot.", + "home.text": "MyAi.ro este o prezentare tehnică pentru sisteme AI practice: înțelegere documente, retrieval, potrivire, automatizare și suport decizional.", + "home.openCv": "Deschide CV Matcher", + "home.step1": "CV.pdf", + "home.step2": "competențe, proiecte, experiență", + "home.step3": "context relevant din CV", + "home.step4": "potrivire job + lipsuri", + "home.navigator": "Navigator", + "home.selectDemo": "Alege un demo AI", + "home.selectText": "Primele noastre projecte", + "tag.available": "Disponibil", + "tag.next": "Urmează", + "demo.cv.title": "CV Matcher", + "demo.cv.text": "Încarcă un CV PDF, adaugă un link sau o descriere de job, extrage context RAG și generează un scor de potrivire cu puncte forte și lipsuri.", + "demo.open": "Deschide demo →", + "demo.rag.title": "RAG Playground", + "demo.rag.text": "Experimentează cu dimensiunea chunk-urilor, numărul de rezultate și transparența surselor.", + "demo.agent.title": "Automatizare cu agenți", + "demo.agent.text": "Fluxuri pentru descoperire joburi, filtrare, ranking și notificări.", + "contact.title": "Discută o integrare AI sau un proiect software custom", + "contact.text": "Complecteaza formularul si te vom contacta.", + "contact.person": "Persoană de contact", + "contact.phone": "Telefon", + "form.name": "Nume", + "form.namePlaceholder": "Numele tău", + "form.email": "Email", + "form.emailPlaceholder": "nume@companie.ro", + "form.message": "Mesaj", + "form.messagePlaceholder": "Spune-mi ce vrei să construiești.", + "form.send": "Trimite mesajul", + "form.thanks": "Mulțumesc pentru mesaj.", + "form.captchaFailed": "Verificarea Captcha a eșuat.", + "form.failed": "Mesajul nu a putut fi trimis.", + "footer.rights": "Toate drepturile rezervate", + "footer.top": "Înapoi sus", + "legal.terms": "Termeni", + "legal.privacy": "Confidențialitate", + "legal.cookies": "Cookies", + "status.sending": "Se trimite...", + "cookies.title": "Cookies", + "cookies.text": "Folosim cookies necesare și, cu acordul tău, analytics prin Google Tag Manager.", + "cookies.policy": "Politica de confidențialitate", + "cookies.reject": "Respinge", + "cookies.necessary": "Doar necesare", + "cookies.accept": "Accept analytics", + "cookies.settings": "Setări cookies", + "cv.brand": "CV Matcher", + "cv.eyebrow": "AI CV Matcher", + "cv.title": "Încarcă CV-ul, adaugă un link de job și vezi cât de bine se potrivesc.", + "cv.text": "Backend-ul ar trebui să extragă textul din PDF, să creeze context RAG din CV, să recupereze experiența relevantă pentru job, apoi să calculeze punctele forte, lipsurile și pașii următori.", + "cv.try": "Încearcă matcherul", + "cv.back": "Înapoi la navigator", + "cv.step1": "Extragere text PDF", + "cv.step2": "Chunking CV + embeddings", + "cv.step3": "Parsare URL/descriere job", + "cv.step4": "Scor potrivire + dovezi", + "cv.input": "Date de intrare", + "cv.details": "CV și detalii job", + "cv.upload": "Încarcă CV PDF", + "cv.fileHint": "Doar PDF, limita de mărime este gestionată de backend", + "cv.jobLink": "Link job", + "cv.jobDescription": "Sau lipește descrierea jobului", + "cv.jobPlaceholder": "Lipește descrierea jobului dacă pagina nu poate fi citită automat.", + "cv.gdpr": "Sunt de acord ca CV-ul meu să fie procesat pentru acest demo de matching. Nu încărca documente sensibile dacă nu ai încredere în deployment.", + "cv.submit": "Extrage CV și compară jobul", + "cv.result": "Rezultat", + "cv.analysis": "Analiză potrivire", + "cv.empty": "Încarcă un CV și adaugă un link sau o descriere de job pentru a genera rezultatul.", + "cv.contactTitle": "Vrei să adaptezi asta pentru workflow-ul tău?", + "cv.contactText": "Formularul folosește endpoint-ul existent de contact din template.", + "cv.noFile": "Te rog încarcă un CV PDF.", + "cv.noJob": "Adaugă un link de job sau lipește descrierea jobului.", + "cv.noConsent": "Consimțământul GDPR este obligatoriu.", + "cv.processing": "Se procesează...", + "cv.extracting": "Se extrage CV-ul și se compară jobul...", + "cv.processingLong": "Se procesează PDF-ul și informațiile despre job. Endpoint-urile backend trebuie să fie disponibile.", + "cv.cvFailed": "Extragerea CV-ului a eșuat", + "cv.matchFailed": "Matching-ul jobului a eșuat", + "cv.completed": "Matching finalizat.", + "cv.backendMissing": "Frontend-ul este pregătit, dar endpoint-urile backend /api/rag/cv și /api/rag/match-job trebuie implementate.", + "cv.noSummary": "Nu a fost returnat niciun sumar.", + "cv.noItems": "Nu au fost returnate elemente.", + "cv.strengths": "Puncte forte", + "cv.gaps": "Lipsuri", + "cv.evidence": "Dovezi recuperate din CV" + } + }; - $('#menuToggle').on('click', function () { - var $nav = $('#mainNav'); - var open = !$nav.hasClass('is-open'); - $nav.toggleClass('is-open', open); - $(this).attr('aria-expanded', open ? 'true' : 'false'); - }); + function browserLang() { + return ((navigator.language || navigator.userLanguage || 'en').toLowerCase().indexOf('ro') === 0) ? 'ro' : 'en'; + } - $('.nav a').on('click', function () { - $('#mainNav').removeClass('is-open'); - $('#menuToggle').attr('aria-expanded', 'false'); - }); + function currentLang() { + return localStorage.getItem(LANG_KEY) || browserLang(); + } - function getRecaptchaWebKey() { - return $.get('/api/contact').done(function (res) { - reCaptchaSiteKey = res; - if (reCaptchaSiteKey && !window.__recaptcha_loaded) { - window.__recaptcha_loaded = true; - var script = document.createElement('script'); - script.setAttribute('src', 'https://www.google.com/recaptcha/api.js?render=' + reCaptchaSiteKey); - document.head.appendChild(script); - } - }).fail(function () { - console.warn('Could not load reCaptcha site key from /api/contact'); - }); - } + function t(key) { + var lang = currentLang(); + return (i18n[lang] && i18n[lang][key]) || i18n.en[key] || key; + } - function getGoogleTagManagerId() { - return $.get('/api/google/tagmanager').done(function (res) { - gTagManagerId = res; - }).fail(function () { - console.warn('Could not load Google Tag Manager id from /api/google/tagmanager'); - }); - } + function updateLegalLinks(lang) { + $('[data-legal]').each(function () { + var page = $(this).data('legal'); + $(this).attr('href', '/legal/' + page + '-' + lang + '.html'); + }); + } - function loadGoogleTagManager() { - if (window.__gtm_loaded || !gTagManagerId) return; - window.__gtm_loaded = true; - window.dataLayer = window.dataLayer || []; - window.dataLayer.push({ 'gtm.start': new Date().getTime(), event: 'gtm.js' }); - var script = document.createElement('script'); - script.async = true; - script.src = 'https://www.googletagmanager.com/gtm/js?id=' + gTagManagerId; - document.head.appendChild(script); - } + function applyLanguage(lang) { + localStorage.setItem(LANG_KEY, lang); + document.documentElement.lang = lang; + updateLegalLinks(lang); + $('[data-i18n]').each(function () { + $(this).text(t($(this).data('i18n'))); + }); + $('[data-i18n-placeholder]').each(function () { + $(this).attr('placeholder', t($(this).data('i18n-placeholder'))); + }); + $('.lang-flag').attr('aria-pressed', 'false'); + $('.lang-flag[data-lang="' + lang + '"]').attr('aria-pressed', 'true'); + } - function getConsent() { - try { return JSON.parse(localStorage.getItem(CONSENT_KEY)); } catch { return null; } - } + $('#year').text(new Date().getFullYear()); + applyLanguage(currentLang()); + $('.lang-flag').on('click', function () { + applyLanguage($(this).data('lang')); + }); - function setConsent(consent) { - localStorage.setItem(CONSENT_KEY, JSON.stringify(consent)); - } + $('#menuToggle').on('click', function () { + var $nav = $('#mainNav'); + var open = !$nav.hasClass('is-open'); + $nav.toggleClass('is-open', open); + $(this).attr('aria-expanded', open ? 'true' : 'false'); + }); + $('.nav a').on('click', function () { + $('#mainNav').removeClass('is-open'); + $('#menuToggle').attr('aria-expanded', 'false'); + }); - function applyConsent(consent) { - if (consent && consent.analytics === true) loadGoogleTagManager(); - } + function getRecaptchaWebKey() { + return $.get('/api/contact').done(function (res) { + reCaptchaSiteKey = res; + if (reCaptchaSiteKey && !window.__recaptcha_loaded) { + window.__recaptcha_loaded = true; + var script = document.createElement('script'); + script.setAttribute('src', 'https://www.google.com/recaptcha/api.js?render=' + reCaptchaSiteKey); + document.head.appendChild(script); + } + }).fail(function () { + console.warn('Could not load reCaptcha site key from /api/contact'); + }); + } - function showBanner() { $('#cookieBanner').fadeIn(200); } - function hideBanner() { $('#cookieBanner').fadeOut(200); } - function showManage() { $('#cookieManage').show(); } + function getGoogleTagManagerId() { + return $.get('/api/google/tagmanager').done(function (res) { + gTagManagerId = res; + }).fail(function () { + console.warn('Could not load Google Tag Manager id from /api/google/tagmanager'); + }); + } - $('#cookieReject, #cookieNecessary').on('click', function () { - setConsent({ necessary: true, analytics: false, ts: new Date().toISOString() }); - hideBanner(); - showManage(); - }); + function loadGoogleTagManager() { + if (window.__gtm_loaded || !gTagManagerId) return; + window.__gtm_loaded = true; + window.dataLayer = window.dataLayer || []; + window.dataLayer.push({ + 'gtm.start': new Date().getTime(), + event: 'gtm.js' + }); + var script = document.createElement('script'); + script.async = true; + script.src = 'https://www.googletagmanager.com/gtm/js?id=' + gTagManagerId; + document.head.appendChild(script); + } - $('#cookieAccept').on('click', function () { - var consent = { necessary: true, analytics: true, ts: new Date().toISOString() }; - setConsent(consent); - applyConsent(consent); - hideBanner(); - showManage(); - }); + function getConsent() { + try { + return JSON.parse(localStorage.getItem(CONSENT_KEY)); + } catch (e) { + return null; + } + } - $('#cookieManage').on('click', function (e) { - e.preventDefault(); - showBanner(); - }); + function setConsent(consent) { + localStorage.setItem(CONSENT_KEY, JSON.stringify(consent)); + } - function initConsent() { - var consent = getConsent(); - if (!consent) showBanner(); - else { applyConsent(consent); showManage(); } - } + function applyConsent(consent) { + if (consent && consent.analytics === true) loadGoogleTagManager(); + } - function submitMSG(valid, msg) { - var msgClasses = valid ? 'form-message text-success' : 'form-message text-danger'; - $('#msgSubmit').removeClass().addClass(msgClasses).text(msg); - } + function showBanner() { + $('#cookieBanner').fadeIn(200); + } - function formSuccess() { - $('#contactForm')[0].reset(); - submitMSG(true, 'Thank you for your message.'); - } + function hideBanner() { + $('#cookieBanner').fadeOut(200); + } - function formError() { - $('#contactForm').removeClass().addClass('contact-form shake').one('animationend', function () { - $(this).removeClass('shake'); - }); - } + function showManage() { + $('#cookieManage').show(); + } + $('#cookieReject, #cookieNecessary').on('click', function () { + setConsent({ + necessary: true, + analytics: false, + ts: new Date().toISOString() + }); + hideBanner(); + showManage(); + }); + $('#cookieAccept').on('click', function () { + var consent = { + necessary: true, + analytics: true, + ts: new Date().toISOString() + }; + setConsent(consent); + applyConsent(consent); + hideBanner(); + showManage(); + }); + $('#cookieManage').on('click', function (e) { + e.preventDefault(); + showBanner(); + }); - $('#contactForm').on('submit', function (event) { - event.preventDefault(); - var loader = $('#contactLoader'); - var button = $('#submit'); - loader.css('display', 'flex'); - button.prop('disabled', true); - $('#msgSubmit').text(''); + function initConsent() { + var consent = getConsent(); + if (!consent) showBanner(); + else { + applyConsent(consent); + showManage(); + } + } - function postContact(token) { - var message = { - Name: $('#name').val(), - Email: $('#email').val(), - Subject: '[MyAi.ro contact request]', - Message: $('#message').val(), - CaptchaToken: token || '' - }; - $.ajax({ - type: 'POST', - url: '/api/contact', - data: JSON.stringify(message), - contentType: 'application/json; charset=utf-8', - dataType: 'json' - }).done(function (resp) { - if (resp && resp.ok === true) formSuccess(); - else submitMSG(false, 'Captcha verification failed.'); - }).fail(function () { - submitMSG(false, 'Failed to send the message.'); - formError(); - }).always(function () { - loader.hide(); - button.prop('disabled', false); - }); - } + function submitMSG(valid, msg) { + $('#msgSubmit').removeClass().addClass(valid ? 'form-message text-success' : 'form-message text-danger').text(msg); + } - if (window.grecaptcha && reCaptchaSiteKey) { - grecaptcha.ready(function () { - grecaptcha.execute(reCaptchaSiteKey, { action: 'contact' }).then(postContact); - }); - } else { - postContact(''); - } - }); + function formSuccess() { + $('#contactForm')[0].reset(); + submitMSG(true, t('form.thanks')); + } - $('#cvFile').on('change', function () { - var file = this.files && this.files[0]; - $('#cvFileName').text(file ? file.name : 'PDF only, max size handled by backend'); - }); + function formError() { + $('#contactForm').removeClass().addClass('contact-form shake').one('animationend', function () { + $(this).removeClass('shake'); + }); + } - $('#cvMatcherForm').on('submit', async function (event) { - event.preventDefault(); - var file = $('#cvFile')[0] && $('#cvFile')[0].files[0]; - var jobUrl = $('#jobUrl').val(); - var jobDescription = $('#jobDescription').val(); - var consent = $('#gdprConsent').is(':checked'); - var $msg = $('#matcherMsg'); - var $button = $('#matchSubmit'); - var $result = $('#matchResult'); + $('#contactForm').on('submit', function (event) { + event.preventDefault(); + var loader = $('#contactLoader'), + button = $('#submit'); + loader.css('display', 'flex'); + button.prop('disabled', true); + $('#msgSubmit').text(''); - if (!file) { $msg.removeClass().addClass('form-message text-danger').text('Please upload a CV PDF.'); return; } - if (!jobUrl && !jobDescription) { $msg.removeClass().addClass('form-message text-danger').text('Add a job link or paste a job description.'); return; } - if (!consent) { $msg.removeClass().addClass('form-message text-danger').text('GDPR consent is required.'); return; } + function postContact(token) { + var message = { + Name: $('#name').val(), + Email: $('#email').val(), + Subject: '[MyAi.ro contact request]', + Message: $('#message').val(), + CaptchaToken: token || '' + }; + $.ajax({ + type: 'POST', + url: '/api/contact', + data: JSON.stringify(message), + contentType: 'application/json; charset=utf-8', + dataType: 'json' + }).done(function (resp) { + if (resp && resp.ok === true) formSuccess(); + else submitMSG(false, t('form.captchaFailed')); + }).fail(function () { + submitMSG(false, t('form.failed')); + formError(); + }).always(function () { + loader.hide(); + button.prop('disabled', false); + }); + } + if (window.grecaptcha && reCaptchaSiteKey) { + grecaptcha.ready(function () { + grecaptcha.execute(reCaptchaSiteKey, { + action: 'contact' + }).then(postContact); + }); + } else { + postContact(''); + } + }); - $button.prop('disabled', true).text('Processing...'); - $msg.removeClass().addClass('form-message').text('Extracting CV and matching job...'); - $result.html('
Processing CV PDF and job input. Backend endpoints must be available.
'); + $('#cvFile').on('change', function () { + var file = this.files && this.files[0]; + $('#cvFileName').text(file ? file.name : t('cv.fileHint')); + }); + $('#cvMatcherForm').on('submit', async function (event) { + event.preventDefault(); + var file = $('#cvFile')[0] && $('#cvFile')[0].files[0]; + var jobUrl = $('#jobUrl').val(); + var jobDescription = $('#jobDescription').val(); + var consent = $('#gdprConsent').is(':checked'); + var $msg = $('#matcherMsg'), + $button = $('#matchSubmit'), + $result = $('#matchResult'); + if (!file) { + $msg.removeClass().addClass('form-message text-danger').text(t('cv.noFile')); + return; + } + if (!jobUrl && !jobDescription) { + $msg.removeClass().addClass('form-message text-danger').text(t('cv.noJob')); + return; + } + if (!consent) { + $msg.removeClass().addClass('form-message text-danger').text(t('cv.noConsent')); + return; + } + $button.prop('disabled', true).text(t('cv.processing')); + $msg.removeClass().addClass('form-message').text(t('cv.extracting')); + $result.html('
' + escapeHtml(t('cv.processingLong')) + '
'); + try { + var formData = new FormData(); + formData.append('cv', file); + formData.append('gdprConsent', String(consent)); + var cvResponse = await fetch('/api/rag/cv', { + method: 'POST', + body: formData + }); + if (!cvResponse.ok) throw new Error(t('cv.cvFailed')); + var cvData = await cvResponse.json(); + var matchResponse = await fetch('/api/rag/match-job', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + cvDocumentId: cvData.documentId || cvData.cvDocumentId, + jobUrl: jobUrl, + jobDescription: jobDescription, + gdprConsent: consent + }) + }); + if (!matchResponse.ok) throw new Error(t('cv.matchFailed')); + var match = await matchResponse.json(); + renderMatchResult(match); + $msg.removeClass().addClass('form-message text-success').text(t('cv.completed')); + } catch (err) { + console.error(err); + $msg.removeClass().addClass('form-message text-danger').text(err.message || t('cv.matchFailed')); + $result.html('
' + escapeHtml(t('cv.backendMissing')) + '
'); + } finally { + $button.prop('disabled', false).text(t('cv.submit')); + } + }); - try { - var formData = new FormData(); - formData.append('cv', file); - formData.append('gdprConsent', String(consent)); + function renderMatchResult(match) { + var score = match.score || match.matchScore || 0; + var summary = match.summary || t('cv.noSummary'); + var strengths = match.strengths || []; + var gaps = match.gaps || match.missingSkills || []; + var evidence = match.evidence || match.retrievedChunks || []; - var cvResponse = await fetch('/api/rag/cv', { method: 'POST', body: formData }); - if (!cvResponse.ok) throw new Error('CV extraction failed'); - var cvData = await cvResponse.json(); + function list(items) { + if (!items || !items.length) return '

' + escapeHtml(t('cv.noItems')) + '

'; + return ''; + } + $('#matchResult').html('
' + Number(score).toFixed(0) + '%

' + escapeHtml(summary) + '

' + t('cv.strengths') + '

' + list(strengths) + '

' + t('cv.gaps') + '

' + list(gaps) + '

' + t('cv.evidence') + '

' + list(evidence)); + } - var matchResponse = await fetch('/api/rag/match-job', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ - cvDocumentId: cvData.documentId || cvData.cvDocumentId, - jobUrl: jobUrl, - jobDescription: jobDescription, - gdprConsent: consent - }) - }); - if (!matchResponse.ok) throw new Error('Job matching failed'); - var match = await matchResponse.json(); - renderMatchResult(match); - $msg.removeClass().addClass('form-message text-success').text('Match completed.'); - } catch (err) { - console.error(err); - $msg.removeClass().addClass('form-message text-danger').text(err.message || 'Failed to run the matcher.'); - $result.html('
The frontend is ready, but the backend endpoints /api/rag/cv and /api/rag/match-job must be implemented.
'); - } finally { - $button.prop('disabled', false).text('Extract CV and match job'); - } - }); - - function renderMatchResult(match) { - var score = match.score || match.matchScore || 0; - var summary = match.summary || 'No summary returned.'; - var strengths = match.strengths || []; - var gaps = match.gaps || match.missingSkills || []; - var evidence = match.evidence || match.retrievedChunks || []; - - function list(items) { - if (!items || !items.length) return '

No items returned.

'; - return ''; - } - - $('#matchResult').html( - '
' + Number(score).toFixed(0) + '%
' + - '

' + escapeHtml(summary) + '

' + - '

Strengths

' + list(strengths) + - '

Gaps

' + list(gaps) + - '

Retrieved CV evidence

' + list(evidence) - ); - } - - function escapeHtml(value) { - return String(value).replace(/[&<>'"]/g, function (char) { - return ({ '&': '&', '<': '<', '>': '>', "'": ''', '"': '"' })[char]; - }); - } - - $(window).on('load', function () { - $.when(getRecaptchaWebKey(), getGoogleTagManagerId()).always(initConsent); - }); -})(jQuery); + function escapeHtml(value) { + return String(value).replace(/[&<>'"]/g, function (char) { + return ({ + '&': '&', + '<': '<', + '>': '>', + "'": ''', + '"': '"' + })[char]; + }); + } + $(window).on('load', function () { + $.when(getRecaptchaWebKey(), getGoogleTagManagerId()).always(initConsent); + }); +})(jQuery); \ No newline at end of file diff --git a/web/wwwroot/legal/cookies-en.html b/web/wwwroot/legal/cookies-en.html index 37ee1ac..eb8d57c 100644 --- a/web/wwwroot/legal/cookies-en.html +++ b/web/wwwroot/legal/cookies-en.html @@ -9,22 +9,24 @@
-
- -
- myAi -
-
myAiLegal pages
-
-
Cookies
diff --git a/web/wwwroot/legal/cookies-ro.html b/web/wwwroot/legal/cookies-ro.html index 74f2cc4..4341744 100644 --- a/web/wwwroot/legal/cookies-ro.html +++ b/web/wwwroot/legal/cookies-ro.html @@ -10,11 +10,13 @@
- -
- myAi -
-
myAiPagini legale
+
+ + MyAi.ro + + + Inapoi +
diff --git a/web/wwwroot/legal/css/legal.css b/web/wwwroot/legal/css/legal.css index e18f636..f70b423 100644 --- a/web/wwwroot/legal/css/legal.css +++ b/web/wwwroot/legal/css/legal.css @@ -1,86 +1,216 @@ -:root{ - --bg:#07192f; - --bg2:#0a2441; - --card:#0e1f37; - --text:#eaf2ff; - --muted:#b4c5dd; - --line:rgba(255,255,255,.10); - --accent:#7eb7ff; -} -*{box-sizing:border-box} -body{ - margin:0; - font-family:Arial,Helvetica,sans-serif; - background: - radial-gradient(circle at top left, rgba(79,140,255,.18), transparent 28%), - linear-gradient(180deg, var(--bg) 0%, #05111f 100%); - color:var(--text); - line-height:1.75; -} -a{color:var(--accent);text-decoration:none} -a:hover{text-decoration:none} -.wrap{max-width:1100px;margin:0 auto;padding:28px 20px 60px} -.topbar{ - display:flex;justify-content:space-between;align-items:center;gap:16px; - padding:10px 0 22px;margin-bottom:24px;border-bottom:1px solid var(--line); -} -.brand{display:flex;align-items:center;gap:14px;} -.brand-badge { - width: 48px; - height: 48px; - background: none; +:root { + --bg: #07192f; + --bg2: #0a2441; + --card: #0e1f37; + --text: #eaf2ff; + --muted: #b4c5dd; + --line: rgba(255,255,255,.10); + --accent: #7eb7ff; } -.brand-badge img { - width: 100%; - height: 100%; - object-fit: contain; +* { + box-sizing: border-box } -.brand-copy small{display:block;color:var(--muted);font-weight:400} -.switcher{display:flex;align-items:center;gap:10px} -.switcher a{ - display:inline-flex;align-items:center;justify-content:center; - width:44px;height:44px;border-radius:999px;border:1px solid var(--line); - background:rgba(255,255,255,.04);transition:.2s ease; +body { + margin: 0; + font-family: Arial,Helvetica,sans-serif; + background: radial-gradient(circle at top left, rgba(79,140,255,.18), transparent 28%), linear-gradient(180deg, var(--bg) 0%, #05111f 100%); + color: var(--text); + line-height: 1.75; } -.switcher a:hover{text-decoration:none;transform:translateY(-1px)} -.switcher a.active{border-color:rgba(126,183,255,.65);box-shadow:0 0 0 3px rgba(126,183,255,.13)} -.switcher img{width:24px;height:24px;display:block} -.hero,.content,.footer{ - background:linear-gradient(180deg, rgba(255,255,255,.03), rgba(255,255,255,.02)); - border:1px solid var(--line); - border-radius:22px; - box-shadow:0 25px 60px rgba(0,0,0,.18); + +a { + color: var(--accent); + text-decoration: none } -.hero{padding:28px 30px;margin-bottom:18px} -.kicker{ - display:inline-block;padding:8px 12px;border-radius:999px; - background:rgba(255,255,255,.04);border:1px solid var(--line);color:var(--muted); - font-size:13px;margin-bottom:12px + + a:hover { + text-decoration: none + } + +.wrap { + max-width: 1100px; + margin: 0 auto; + padding: 28px 20px 60px } -.hero h1{margin:0 0 8px;font-size:40px;line-height:1.08} -.hero p{margin:0;color:var(--muted);max-width:780px} -.content{padding:30px} -.content h2{font-size:28px;line-height:1.15;margin:28px 0 10px} -.content h2:first-child{margin-top:0} -.content p{margin:0 0 14px;color:#deebff} -.content ul{margin:0 0 18px 22px;padding:0} -.content li{margin-bottom:8px;color:#deebff} -.notice{ - margin:16px 0 18px;padding:16px 18px;border-radius:18px; - background:rgba(126,183,255,.08);border:1px solid rgba(126,183,255,.18) + +.topbar { + display: flex; + justify-content: space-between; + align-items: center; + gap: 16px; + padding: 10px 0 22px; + margin-bottom: 24px; + border-bottom: 1px solid var(--line); } -.footer{ - margin-top:18px;padding:22px 26px;display:flex;justify-content:space-between;gap:16px;align-items:center;flex-wrap:wrap + +.brand { + display: flex; + align-items: center; + gap: 14px; } -.footer-links{display:flex;gap:18px;flex-wrap:wrap} -.footer small{color:var(--muted)} -.meta{color:var(--muted);font-size:14px} -@media (max-width:768px){ - .hero h1{font-size:32px} - .content{padding:22px} - .content h2{font-size:24px} - .topbar{align-items:flex-start;flex-direction:column} + +.brand-text { + font-size: 1.35rem +} + +.brand small { + display: none +} + +.brand-mark { + width: 42px; + height: 42px +} + +.switcher { + display: flex; + align-items: center; + gap: 10px +} + + .switcher a { + display: inline-flex; + align-items: center; + justify-content: center; + width: 44px; + height: 44px; + border-radius: 999px; + border: 1px solid var(--line); + background: rgba(255,255,255,.04); + transition: .2s ease; + } + + .switcher a:hover { + text-decoration: none; + transform: translateY(-1px) + } + + .switcher a.active { + border-color: rgba(126,183,255,.65); + box-shadow: 0 0 0 3px rgba(126,183,255,.13) + } + + .switcher img { + width: 24px; + height: 24px; + display: block + } + +.hero, .content, .footer { + background: linear-gradient(180deg, rgba(255,255,255,.03), rgba(255,255,255,.02)); + border: 1px solid var(--line); + border-radius: 22px; + box-shadow: 0 25px 60px rgba(0,0,0,.18); +} + +.hero { + padding: 28px 30px; + margin-bottom: 18px +} + +.kicker { + display: inline-block; + padding: 8px 12px; + border-radius: 999px; + background: rgba(255,255,255,.04); + border: 1px solid var(--line); + color: var(--muted); + font-size: 13px; + margin-bottom: 12px +} + +.hero h1 { + margin: 0 0 8px; + font-size: 40px; + line-height: 1.08 +} + +.hero p { + margin: 0; + color: var(--muted); + max-width: 780px +} + +.content { + padding: 30px +} + + .content h2 { + font-size: 28px; + line-height: 1.15; + margin: 28px 0 10px + } + + .content h2:first-child { + margin-top: 0 + } + + .content p { + margin: 0 0 14px; + color: #deebff + } + + .content ul { + margin: 0 0 18px 22px; + padding: 0 + } + + .content li { + margin-bottom: 8px; + color: #deebff + } + +.notice { + margin: 16px 0 18px; + padding: 16px 18px; + border-radius: 18px; + background: rgba(126,183,255,.08); + border: 1px solid rgba(126,183,255,.18) +} + +.footer { + margin-top: 18px; + padding: 22px 26px; + display: flex; + justify-content: space-between; + gap: 16px; + align-items: center; + flex-wrap: wrap +} + +.footer-links { + display: flex; + gap: 18px; + flex-wrap: wrap +} + +.footer small { + color: var(--muted) +} + +.meta { + color: var(--muted); + font-size: 14px +} + +@media (max-width:768px) { + .hero h1 { + font-size: 32px + } + + .content { + padding: 22px + } + + .content h2 { + font-size: 24px + } + + .topbar { + align-items: flex-start; + flex-direction: column + } } diff --git a/web/wwwroot/legal/privacy-en.html b/web/wwwroot/legal/privacy-en.html index 531ecf6..2e2c29e 100644 --- a/web/wwwroot/legal/privacy-en.html +++ b/web/wwwroot/legal/privacy-en.html @@ -9,22 +9,24 @@