This commit is contained in:
johny
2026-01-29 00:04:52 +01:00
commit edac3b3836
24 changed files with 1819 additions and 0 deletions

24
.gitignore vendored Normal file
View File

@@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

14
index.html Normal file
View File

@@ -0,0 +1,14 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>medforum</title>
<script src="/config.js"></script>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
</body>
</html>

1245
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

19
package.json Normal file
View File

@@ -0,0 +1,19 @@
{
"name": "medforum",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
},
"devDependencies": {
"@vitejs/plugin-vue2": "^2.3.4",
"vite": "^7.2.4"
},
"dependencies": {
"vue": "^2.7.16",
"vue-template-compiler": "^2.7.16"
}
}

26
public/api.json Normal file
View File

@@ -0,0 +1,26 @@
[
{
"title": "Komunikacja",
"content": "Łączymy nowoczesne działania związane z komunikacją oraz wiedzą medyczną, tworząc skuteczne narzędzia promocyjno-edukacyjne. Docieramy do lekarzy, farmaceutów, pielęgniarek i położnych oraz innych specjalistów ochrony zdrowia, a także pacjentów z wartościowymi, precyzyjnie dopasowanymi treściami. Współpracujemy z ekspertami i liderami opinii.",
"color": "#6AB971",
"image": "doctor.png"
},
{
"title": "Marketing",
"content": "Projektujemy cyfrowe i analogowe działania marketingowe oparte na analizie danych i doświadczeniu dla rynku medycznego. Wykorzystujemy badania focusowe, strategie SEO, SEM, content i e-mail marketing, by skutecznie wspierać marki medyczne i farmaceutyczne. Realizujemy dla klienta działania marketingowe oparte na aktualnej wiedzy i trendach. Mamy długoletnie doświadczenie w realizacji działań w kategoriach ATL i BTL.",
"color": "#44A5D3",
"image": "woman.png"
},
{
"title": "Sprzedaż",
"content": "Wspieramy sprzedaż poprzez wykorzystanie unikalnych narzędzi. Realizujemy kampanie sprzedażowo-marketingowe oraz dystrybucję materiałów edukacyjnych. Pomagamy skutecznie docierać do odbiorców i wspierać realizację założonych celów.",
"color": "#F3AA21CF",
"image": "man.png"
},
{
"title": "Strategia",
"content": "Tworzymy kompleksowe strategie marketingowe i komunikacyjne w oparciu o wieloletnie doświadczenie i znajomość rynku medycznego. Każde rozwiązanie dopasowujemy do celów i potrzeb klienta. Bazujemy na analizie danych, trendach i potrzebach grupy docelowej z uwzględnieniem odbiorcy indywidualnego i masowego.",
"color": "#F58220",
"image": "girl.png"
}
]

1
public/config.js Symbolic link
View File

@@ -0,0 +1 @@
/home/johny/projects/medforum/config.js

BIN
public/images/doctor.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 765 KiB

BIN
public/images/girl.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 MiB

BIN
public/images/man.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

BIN
public/images/woman.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

17
public/index.php Normal file
View File

@@ -0,0 +1,17 @@
<?php
$uri = $_SERVER['REQUEST_URI'];
if($uri == '/api'){
header('Content-Type: application/json; charset=utf-8');
header('Access-Control-Allow-Origin: *');
$json = json_decode(file_get_contents('./api.json'), true);
foreach ($json as $key => $value) {
$json[$key]['image'] = '//'.$_SERVER['SERVER_NAME'].':'.$_SERVER['SERVER_PORT'].'/images/'.$value['image'];
}
echo json_encode($json);
die();
}
if($uri == '/'){
header('Location: /index.html');
die();
}

1
public/vite.svg Normal file
View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

424
src/App.vue Normal file
View File

@@ -0,0 +1,424 @@
<template>
<div class="container">
<h1>Wsparcie i obsługa organizacji 360<sup>o</sup></h1>
<main>
<div class="bg-image-container">
<img
v-for="(item, i) in items"
:key="i"
:src="item.image"
v-show="index === i"
class="bg-image"
alt=""
/>
<!-- <img v-if="items.length" :src="currentItem.image" class="bg-image" alt="" />-->
</div>
<div style="width: 100%; height: 100%;display: flex;flex-direction: column;align-items: center;justify-content: center;">
<div id="circle">
<div id="content">
<div :style="{ background: items[0] ? items[0].color : '#E6E6E6' }"></div>
<div :style="{ background: items[1] ? items[1].color : '#E6E6E6' }"></div>
<div :style="{ background: items[3] ? items[3].color : '#E6E6E6' }"></div>
<div :style="{ background: items[2] ? items[2].color : '#E6E6E6' }"></div>
</div>
<div id="con">
<div id="text" v-if="index !== null">
<div v-if="items.length && currentItem">
<h2>{{currentItem.title}}</h2>
<p>{{currentItem.content}}</p>
</div>
</div>
<div class="logo">
<img src="./assets/logo.png" alt="logo">
</div>
</div>
<div id="dot1" class="dot">
<div :style="{ background: items[0] && index === 0 ? items[0].color : '#E6E6E6' }" @click="setIndex(0)"></div>
<span>{{getTitle(0)}}</span>
</div>
<div id="dot2" class="dot">
<div :style="{ background: items[1] && index === 1 ? items[1].color : '#E6E6E6' }" @click="setIndex(1)"></div>
<span>{{getTitle(1)}}</span>
</div>
<div id="dot3" class="dot">
<div :style="{ background: items[2] && index === 2 ? items[2].color : '#E6E6E6' }" @click="setIndex(2)"></div>
<span>{{getTitle(2)}}</span>
</div>
<div id="dot4" class="dot">
<div :style="{ background: items[3] && index === 3 ? items[3].color : '#E6E6E6' }" @click="setIndex(3)"></div>
<span>{{getTitle(3)}}</span>
</div>
<div style="width: 100%; height: 100%;overflow: hidden;">
<svg style="z-index: -10;" id="circleBox" width="100%" height="100%" viewBox="0 0 931 931" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="465.311" cy="465.311" r="390.817" transform="rotate(-102 465.311 465.311)" stroke="#E6E6E6" stroke-width="3"/>
<path d="M132.944 259.067C19.1825 443.492 76.4666 685.22 260.891 798.982" stroke="url(#paint0_linear_4001_12)" stroke-width="4"/>
<path d="M137.046 268.413C137.685 268.168 138.015 267.468 137.815 266.82L137.813 266.812L137.81 266.805L134.677 257.655L134.676 257.649C134.457 257.034 133.822 256.675 133.182 256.805L133.176 256.806L123.721 258.846L123.722 258.847C123.023 258.995 122.572 259.691 122.718 260.388C122.865 261.088 123.562 261.541 124.26 261.394L124.263 261.394L130.883 259.969L122.041 275.622C121.688 276.247 121.909 277.044 122.534 277.397C123.159 277.75 123.957 277.529 124.31 276.904L133.153 261.251L135.35 267.657L135.356 267.676C135.621 268.351 136.379 268.671 137.046 268.413L137.046 268.413Z" fill="#181818" stroke="black"/>
<defs>
<linearGradient id="paint0_linear_4001_12" x1="128.306" y1="266.587" x2="195.299" y2="778.339" gradientUnits="userSpaceOnUse">
<stop stop-color="#181818"/>
<stop offset="0.7" stop-color="#7E7E7E" stop-opacity="0"/>
</linearGradient>
</defs>
</svg>
</div>
</div>
<div class="offerUrl">
<a href="#">poznaj naszą ofertę</a>
</div>
</div>
<div class="text" v-if="index !== null">
<div v-if="items.length && currentItem">
<h2>{{currentItem.title}}</h2>
<p>{{currentItem.content}}</p>
<div style="margin-top: 2rem">
<a href="#">poznaj naszą ofertę</a>
</div>
</div>
</div>
</main>
<img src="./assets/dots.png" alt="" class="dots">
</div>
</template>
<script>
export default {
name: 'App',
data() {
return {
index: null,
timer: null,
rotation: 0,
box: null,
items: [],
loaded: false,
}
},
computed: {
currentItem() {
return this.items[this.index];
},
},
methods: {
nextIndex() {
if (this.index < this.items.length - 1) {
this.setIndex(this.index+1)
} else {
this.setIndex(0);
}
},
autoplay() {
this.timer = setInterval(this.nextIndex, 5000);
},
stopAutoplay() {
clearInterval(this.timer)
},
getTitle(index) {
return this.items[index]?.title;
},
setIndex(index) {
if(index !== this.index) {
let steps = index - this.index;
if(steps < 0) steps = this.items.length + steps;
this.rotation += steps * 90;
this.box.style.transition = 'transform 0.3s linear';
this.box.style.transform = `rotate(${this.rotation}deg)`;
this.index = index;
}
}
},
mounted() {
this.box = document.getElementById('circleBox');
const apiUrl = (window.API_CONFIG && window.API_CONFIG.API_URL)
? window.API_CONFIG.API_URL
: 'http://localhost:8003/api';
fetch(apiUrl)
.then(response => response.json())
.then(data => {
this.items = data;
this.autoplay();
})
.catch(error => console.error("Błąd ładowania danych:", error));
},
beforeDestroy() {
this.stopAutoplay()
}
}
</script>
<style scoped>
.container {
--default-color: #E6E6E6;
--color1: #6AB971;
--color3: #44A5D3;
--color4: #F3AA21CF;
--color2: #F58220;
height: 100%;
display: flex;
flex-direction: column;
padding-inline: 2rem;
}
h1 {
text-align: center;
padding-block: 2rem;
font-size: 4.125rem;
text-wrap: balance;
}
h2 {
text-align: center;
padding-block: 1rem;
}
main {
margin-inline: 5%;
flex-grow: 1;
display: grid;
grid-template-columns: 2fr 3fr;
align-items: end;
height: 100%;
}
#circle {
width: min(100%, 80vh);
height: auto;
aspect-ratio: 1/1;
position: relative;
}
#content {
position: absolute;
top: 15%;
left: 15%;
right: 15%;
bottom: 15%;
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 10px;
div:nth-child(1) {
background: var(--default-color);
border-radius: 100% 0 0 0;
}
div:nth-child(2) {
background: var(--default-color);
border-radius: 0 100% 0 0;
}
div:nth-child(3) {
background: var(--default-color);
border-radius: 0 0 0 100%;
}
div:nth-child(4) {
background: var(--default-color);
border-radius: 0 0 100% 0;
}
}
#con {
position: absolute;
top: 20%;
left: 20%;
right: 20%;
bottom: 20%;
background-color: white;
border-radius: 100%;
z-index: 100;
}
#text {
width: 100%;
height: 100%;
div {
height: 70%;
margin: 15%;
overflow: scroll;
}
p {
text-align: center;
}
z-index: 110;
line-height: 1.5;
h2 {
font-size: 1.625rem;
}
}
#text div::-webkit-scrollbar {
width: 4px;
}
#text div::-webkit-scrollbar-thumb {
background: #ccc;
border-radius: 10px;
}
.dot {
position: absolute;
display: flex;
align-items: center;
height: 10%;
z-index: 150;
div {
height: 100%;
border-radius: 50%;
aspect-ratio: 1/1;
background: var(--default-color);
cursor: pointer;
}
span {
font-size: 1.625rem;
font-weight: bold;
}
}
#dot1, #dot4 {
left: 25%;
flex-direction: row-reverse;
transform: translateX(-100%);
div {
margin-left: 10px;
}
}
#dot2, #dot3 {
right: 25%;
flex-direction: row;
transform: translateX(100%);
div {
margin-right: 10px;
}
}
#dot1 {
top: 15%;
}
#dot2 {
top: 15%;
}
#dot3 {
bottom: 15%;
}
#dot4 {
bottom: 15%;
}
.text {
display: none;
}
.bg-image-container {
position: relative;
}
.bg-image {
position: fixed;
bottom: 0;
left: 5%;
width: calc(90% * 2/5);
height: auto;
max-height: 90vh;
object-fit: contain;
}
.dots {
position: fixed;
top: 0;
left: 0;
z-index: -1;
transform: translateX(-50%) translateY(-30%);
}
.logo {
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
img {
width: 100%;
object-fit: contain;
}
}
#text ~ .logo {
display: none;
}
a {
display: block;
margin-bottom: 2rem;
font-weight: bold;
text-decoration: underline;
color: black;
font-size: 1.375rem;
}
@media screen and (max-width: 1440px) {
h1 {
font-size: 3rem;
}
.dot span {
font-size: 1.375rem;
}
}
@media screen and (max-width: 999px) {
.offerUrl {
display: none;
}
.container {
margin-inline: 60px
}
h1 {
font-size: 2.75rem;
}
#text {
display: none;
}
.bg-image-container {
display: none;
}
.dots {
display: none;
}
main {
grid-template-columns: 1fr;
flex-grow: 0;
height: auto;
display: flex;
flex-direction: column;
align-items: center;
}
#text ~ .logo {
display: flex; /* lub flex/inline, zależnie od Twojego logo */
}
.text {
padding-bottom: 2rem;
display: block;
h2 {
font-size: 1.25rem;
}
p {
font-size: 0.875rem;
}
}
a {
font-size: 1.25rem;
text-align: center;
}
}
@media screen and (max-width: 767px) {
h1 {
font-size: 2rem;
}
a {
font-size: 1.125rem;
}
.container .dot span {
font-size: 1rem;
}
}
@media screen and (max-width: 479px) {
a {
font-size: 1rem;
}
.container .dot span {
font-size: 0.875rem;
}
}
</style>

BIN
src/assets/doctor.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 765 KiB

BIN
src/assets/dots.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

BIN
src/assets/girl.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 MiB

BIN
src/assets/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

BIN
src/assets/man.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

BIN
src/assets/woman.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

9
src/counter.js Normal file
View File

@@ -0,0 +1,9 @@
export function setupCounter(element) {
let counter = 0
const setCounter = (count) => {
counter = count
element.innerHTML = `count is ${counter}`
}
element.addEventListener('click', () => setCounter(counter + 1))
setCounter(0)
}

1
src/javascript.svg Normal file
View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="32" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path fill="#F7DF1E" d="M0 0h256v256H0V0Z"></path><path d="m67.312 213.932l19.59-11.856c3.78 6.701 7.218 12.371 15.465 12.371c7.905 0 12.89-3.092 12.89-15.12v-81.798h24.057v82.138c0 24.917-14.606 36.259-35.916 36.259c-19.245 0-30.416-9.967-36.087-21.996m85.07-2.576l19.588-11.341c5.157 8.421 11.859 14.607 23.715 14.607c9.969 0 16.325-4.984 16.325-11.858c0-8.248-6.53-11.17-17.528-15.98l-6.013-2.58c-17.357-7.387-28.87-16.667-28.87-36.257c0-18.044 13.747-31.792 35.228-31.792c15.294 0 26.292 5.328 34.196 19.247l-18.732 12.03c-4.125-7.389-8.591-10.31-15.465-10.31c-7.046 0-11.514 4.468-11.514 10.31c0 7.217 4.468 10.14 14.778 14.608l6.014 2.577c20.45 8.765 31.963 17.7 31.963 37.804c0 21.654-17.012 33.51-39.867 33.51c-22.339 0-36.774-10.654-43.819-24.574"></path></svg>

After

Width:  |  Height:  |  Size: 995 B

6
src/main.js Normal file
View File

@@ -0,0 +1,6 @@
import Vue from 'vue'
import App from './App.vue'
import './style.css'
new Vue({
render: h => h(App),
}).$mount('#app')

23
src/style.css Normal file
View File

@@ -0,0 +1,23 @@
@import url('https://fonts.googleapis.com/css2?family=Roboto:wght@400;700&display=swap');
html {
background-color: #F7F7F9;
}
* {
margin: 0;
padding: 0;
}
html, body {
margin: 0;
padding: 0;
font-family: 'Roboto', sans-serif;
min-height: 100vh;
height: 100%;
}
/*body {*/
/* background-image: url('/images/7c5a1382b99fb79a82643ae289962e8b69336b2e.png');*/
/* background-repeat: no-repeat;*/
/* background-position: left bottom;*/
/* background-attachment: fixed;*/
/* background-size: auto 80%;*/
/*}*/

9
vite.config.js Normal file
View File

@@ -0,0 +1,9 @@
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue2'
export default defineConfig({
plugins: [vue()],
server: {
hmr: true,
}
})