Migracja projektu na Vite

This commit is contained in:
2022-08-17 23:07:21 +02:00
commit c799b47698
26 changed files with 2222 additions and 0 deletions
+131
View File
@@ -0,0 +1,131 @@
<template>
<div class="login">
<div class="login-header">
<img src="/icon-logo.svg" alt="logo" />
<h1>Stacjownik Station Manager</h1>
</div>
<form @submit="signIn">
<label for="name">Nick</label>
<br />
<input type="text" id="name" v-model="name" />
<br />
<label for="password">Hasło</label>
<br />
<input type="password" id="password" v-model="password" />
<br />
<button>Zaloguj</button>
</form>
</div>
</template>
<script lang="ts">
import axios from 'axios';
import { defineComponent } from 'vue';
import dataMixin from '../mixins/dataMixin';
import { useStore } from '../store';
interface LoginResponse {
token: string;
user: {
name: string;
id: string;
};
}
export default defineComponent({
mixins: [dataMixin],
setup() {
return {
name: '',
password: '',
store: useStore(),
};
},
methods: {
async signIn(e: Event) {
e.preventDefault();
console.log(import.meta.env.VITE_API_URL);
try {
const data: LoginResponse = (
await axios.post(
`${this.API_URL}/auth/login`,
{ username: this.name, password: this.password },
{
headers: {
'Content-Type': 'application/json',
},
}
)
).data;
this.store.isAuthorized = true;
this.store.token = data.token;
this.store.user = data.user;
window.localStorage.setItem('auth-token', this.store.token);
window.localStorage.setItem('user', JSON.stringify(this.store.user));
this.loadData();
this.$router.push('/');
} catch (e: any) {
const response = e.response;
const status: number = response.status;
if (status == 401) {
this.store.alertMessage = 'Nieprawidłowe dane!';
return;
}
this.store.alertMessage = 'Wystąpił błąd podczas łączenia z serwerem!';
return;
}
},
},
});
</script>
<style lang="scss" scoped>
.login {
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
height: 100vh;
&-header {
text-align: center;
}
}
img {
width: 8em;
}
input {
font-size: 1.2rem;
margin: 0.5rem 0;
padding: 0.25em 0.3em;
color: black;
}
h2 {
text-align: center;
}
label {
text-align: left;
}
button {
width: 100%;
margin: 1rem 0;
padding: 0.5rem 0;
font-size: 1rem;
}
</style>
+253
View File
@@ -0,0 +1,253 @@
<template>
<div class="manager">
<RoutesModal v-if="store.currentStation" />
<hr color="white" />
<TableActions />
<hr color="white" />
<div class="table_container">
<table>
<thead>
<th v-for="header in headerNameList">{{ header }}</th>
<th>Dostępność</th>
</thead>
<tbody>
<tr v-for="(station, row) in sortedStationList" tabindex="0">
<td v-for="(value, propName) in headerNameList" @click="changeProperty(station, row, propName as string)">
<span v-if="propName === 'url'" :style="station.url ? 'color: gold' : 'color: gray;'">URL</span>
<span v-else-if="propName === 'checkpoints'">{{ station[propName] ? 'POKAŻ' : 'DODAJ' }}</span>
<span v-else-if="propName === 'routes'" v-html="getRouteNames(station)"></span>
<span v-else-if="typeof (station as any)[propName] === 'boolean' && propName !== 'supportersOnly'">
{{ (station as any)[propName] ? '' : '' }}
</span>
<span v-else>{{ (station as any)[propName] }}</span>
</td>
<td>
<select
name="availability"
:id="`select-${row}`"
v-model="sortedStationList[row]['availability']"
@input="(e) => changeAvailability(station.name, sortedStationList[row]['availability'], e)"
>
<option value="default">dostępna (w paczce)</option>
<option value="nonDefault">dostępna (poza paczką)</option>
<option value="unavailable">niedostępna</option>
<option value="nonPublic">niepubliczna</option>
<option value="abandoned">wycofana</option>
</select>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import changeMixin from '../mixins/changeMixin';
import dataMixin from '../mixins/dataMixin';
import { useStore } from '../store';
import { SceneryRowItem, Availability } from '../types/types';
import RoutesModal from '../components/RoutesModal.vue';
import TableActions from '../components/TableActions.vue';
export default defineComponent({
components: { RoutesModal, TableActions },
mixins: [dataMixin, changeMixin],
data: () => ({
headerNameList: {
name: 'Nazwa',
url: 'URL',
lines: 'Linie',
project: 'Projekt',
reqLevel: 'Wym. poziom',
signalType: 'Sygnalizacja',
controlType: 'Sterowanie',
SUP: 'SUP',
authors: 'Autorzy',
routes: 'Szlaki',
checkpoints: 'Posterunki',
} as {
[key: string]: string;
},
}),
setup() {
const store = useStore();
return {
store,
};
},
mounted() {
this.loadData();
},
computed: {
sortedStationList() {
const sortedList = this.store.stationList.sort((a, b) => (a.name > b.name ? 1 : -1));
if (!this.store.searchedSceneryName || this.store.searchedSceneryName == '') return sortedList;
return sortedList.filter((station) =>
station.name.toLowerCase().startsWith(this.store.searchedSceneryName.toLowerCase())
);
},
},
methods: {
addNewStation() {
this.store.newStationsCount++;
const newSt: SceneryRowItem = {
name: `${this.store.newStationsCount}_Sceneria`,
url: '',
lines: '',
project: null,
reqLevel: 0,
signalType: '',
controlType: '',
SUP: false,
routes: '',
checkpoints: '',
authors: '',
availability: 'default',
};
this.store.stationList.unshift(newSt);
},
getRouteNames(station: SceneryRowItem) {
if (!station.routes) return '';
return station.routes
.split(';')
.map((route) => {
// !Oc_2EPB
const props1 = route.split('_')[0];
const props2 = route.split('_')[1];
const isInternal = props1.startsWith('!');
const name = isInternal ? props1.replace('!', '') : props1;
return `${isInternal ? '<u>' + name + '</u>' : name} <span style='color: #aaa'>(${props2[0]}/${props2[1]}/${
props2[2]
}${props2[3] ? '/B' : ''})</span>`;
})
.join(', ');
},
changeProperty(station: SceneryRowItem, row: number, propertyName: string) {
this.store.selectedStationName = station.name;
if (propertyName == 'name') return;
if (propertyName == 'checkpoints') {
this.changeCheckpoints(row);
return;
}
if (propertyName == 'routes') {
this.showRoutesModal(station);
return;
}
const stationListRow = this.store.stationList.findIndex(
(station) => station.name == this.sortedStationList[row].name
);
if (stationListRow == -1) return;
const oldValue = (this.store.stationList[stationListRow] as any)[propertyName];
if (typeof oldValue === 'boolean') {
(this.store.stationList[stationListRow] as any)[propertyName] = !oldValue;
// this.$set(this.stationList[stationListRow], propertyName, !oldValue);
this.addChange(station.name, propertyName, oldValue, !oldValue);
return;
}
let newValue = prompt(`Zmień wartość dla rubryki ${this.headerNameList[propertyName]}`, oldValue);
if (newValue == null) return;
(this.store.stationList[stationListRow] as any)[propertyName] =
typeof oldValue === 'number' ? parseInt(newValue) : newValue;
// this.$set(this.stationList[stationListRow], propertyName, parseInt(newValue));
this.addChange(
station.name,
propertyName,
oldValue,
typeof oldValue === 'number' ? parseInt(newValue) : newValue
);
},
changeCheckpoints(row: number) {
const stationListRow = this.store.stationList.findIndex(
(station) => station.name == this.sortedStationList[row].name
);
if (stationListRow == -1) return;
const oldCheckpoints = this.store.stationList[stationListRow].checkpoints;
const newCheckpoints = prompt('Wpisz posterunki (oddzielone średnikiem):', oldCheckpoints);
if (newCheckpoints === null) return;
this.store.stationList[stationListRow]['checkpoints'] = newCheckpoints;
this.addChange(this.sortedStationList[row].name, 'checkpoints', oldCheckpoints, newCheckpoints);
},
changeAvailability(stationName: string, availability: Availability, e: Event) {
const selectedAvailability: Availability = (e.target as HTMLSelectElement).value as Availability;
this.addChange(stationName, 'availability', availability, selectedAvailability);
},
showRoutesModal(station: SceneryRowItem) {
this.store.currentStation = station;
},
},
});
</script>
<style lang="scss" scoped>
.table_container {
overflow: auto;
height: 100vh;
margin: 0.5em 0;
}
table {
text-align: center;
color: white;
position: relative;
border-collapse: collapse;
width: 100%;
max-width: 2000px;
min-width: 1450px;
}
table thead {
position: sticky;
top: 0;
}
table th {
padding: 0.4rem 0.45rem;
background-color: #151b24;
color: white;
top: 0;
}
table tr {
background-color: #2c394b;
transition: background-color 100ms;
}
table tr.current {
outline: 1px solid white;
}
table tr:nth-child(even) {
background-color: #334756;
}
table tr:hover {
background-color: #1a293b;
cursor: pointer;
}
table tr td {
padding: 0.3rem 0.5rem;
border: 1px solid #2c2c2c;
overflow: auto;
text-overflow: ellipsis;
}
</style>