mirror of
https://github.com/Spythere/stacjownik.git
synced 2026-05-03 13:28:11 +00:00
Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 471e6f5216 | |||
| a617eef00e | |||
| 38e700ecd6 | |||
| da1be0e10a | |||
| f49bb12948 | |||
| 02673a3d70 | |||
| 4ddc7345df | |||
| 5d822684c0 |
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "stacjownik",
|
"name": "stacjownik",
|
||||||
"version": "1.14.1",
|
"version": "1.14.3",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M23.75 3.75H22.5V1.25H20V3.75H10V1.25H7.5V3.75H6.25C4.875 3.75 3.75 4.875 3.75 6.25V23.75C3.75 25.125 4.875 26.25 6.25 26.25H23.75C25.125 26.25 26.25 25.125 26.25 23.75V6.25C26.25 4.875 25.125 3.75 23.75 3.75ZM23.75 23.75H6.25V11.25H23.75V23.75ZM6.25 8.75V6.25H23.75V8.75H6.25ZM8.75 13.75H21.25V16.25H8.75V13.75ZM8.75 18.75H17.5V21.25H8.75V18.75Z" fill="#F2E147"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 477 B |
@@ -0,0 +1,3 @@
|
|||||||
|
<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M23.75 3.75H22.5V1.25H20V3.75H10V1.25H7.5V3.75H6.25C4.875 3.75 3.75 4.875 3.75 6.25V23.75C3.75 25.125 4.875 26.25 6.25 26.25H23.75C25.125 26.25 26.25 25.125 26.25 23.75V6.25C26.25 4.875 25.125 3.75 23.75 3.75ZM23.75 23.75H6.25V11.25H23.75V23.75ZM6.25 8.75V6.25H23.75V8.75H6.25ZM8.75 13.75H21.25V16.25H8.75V13.75ZM8.75 18.75H17.5V21.25H8.75V18.75Z" fill="#66FF6C"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 477 B |
@@ -0,0 +1,3 @@
|
|||||||
|
<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M23.75 3.75H22.5V1.25H20V3.75H10V1.25H7.5V3.75H6.25C4.875 3.75 3.75 4.875 3.75 6.25V23.75C3.75 25.125 4.875 26.25 6.25 26.25H23.75C25.125 26.25 26.25 25.125 26.25 23.75V6.25C26.25 4.875 25.125 3.75 23.75 3.75ZM23.75 23.75H6.25V11.25H23.75V23.75ZM6.25 8.75V6.25H23.75V8.75H6.25ZM8.75 13.75H21.25V16.25H8.75V13.75ZM8.75 18.75H17.5V21.25H8.75V18.75Z" fill="#898989"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 477 B |
@@ -8,7 +8,12 @@
|
|||||||
<table>
|
<table>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th v-for="(headerName, i) in headIds" :key="headerName" @click="changeSorter(headerName)">
|
<th
|
||||||
|
v-for="(headerName, i) in headIds"
|
||||||
|
:key="headerName"
|
||||||
|
@click="changeSorter(headerName)"
|
||||||
|
class="header-text"
|
||||||
|
>
|
||||||
<span class="header_wrapper">
|
<span class="header_wrapper">
|
||||||
<div v-html="$t(`sceneries.${headerName}`)"></div>
|
<div v-html="$t(`sceneries.${headerName}`)"></div>
|
||||||
|
|
||||||
@@ -21,9 +26,14 @@
|
|||||||
</span>
|
</span>
|
||||||
</th>
|
</th>
|
||||||
|
|
||||||
<th v-for="(headerName, i) in headIconsIds" :key="headerName" @click="changeSorter(headerName)">
|
<th
|
||||||
|
v-for="(headerName, i) in headIconsIds"
|
||||||
|
:key="headerName"
|
||||||
|
@click="changeSorter(headerName)"
|
||||||
|
class="header-image"
|
||||||
|
>
|
||||||
<span class="header_wrapper">
|
<span class="header_wrapper">
|
||||||
<img :src="getIcon(headerName)" :alt="headerName" :title="$t(`sceneries.${headerName}s`)" />
|
<img :src="getIcon(headerName)" :alt="headerName" :title="$t(`sceneries.${headerName}`)" />
|
||||||
|
|
||||||
<img
|
<img
|
||||||
class="sort-icon"
|
class="sort-icon"
|
||||||
@@ -190,25 +200,31 @@
|
|||||||
|
|
||||||
<td class="station_users" :class="{ inactive: !station.onlineInfo }">
|
<td class="station_users" :class="{ inactive: !station.onlineInfo }">
|
||||||
<span>
|
<span>
|
||||||
<span class="highlight">{{ station.onlineInfo?.currentUsers || '0' }}</span>
|
<span class="highlight">{{ station.onlineInfo?.currentUsers || 0 }}</span>
|
||||||
/
|
/
|
||||||
<span>{{ station.onlineInfo?.maxUsers || '0' }}</span>
|
<span class="highlight">{{ station.onlineInfo?.maxUsers || 0 }}</span>
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
<td class="station_spawns" :class="{ inactive: !station.onlineInfo }">
|
<td class="station_spawns" :class="{ inactive: !station.onlineInfo }">
|
||||||
<span class="highlight">{{ station.onlineInfo?.spawns.length || '0' }}</span>
|
<span>{{ station.onlineInfo?.spawns.length || 0 }}</span>
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
<td class="station_schedules" :class="{ inactive: !station.onlineInfo }">
|
<td class="station_schedules" style="width: 30px" :class="{ inactive: !station.onlineInfo }">
|
||||||
<span>
|
<span class="highlight">
|
||||||
<span class="highlight">
|
{{ station.onlineInfo?.scheduledTrains?.length || 0 }}
|
||||||
{{ station.onlineInfo?.scheduledTrains?.length || '0' }}
|
</span>
|
||||||
</span>
|
</td>
|
||||||
/
|
|
||||||
<span style="color: #bbb">
|
<td class="station_schedules" style="width: 30px" :class="{ inactive: !station.onlineInfo }">
|
||||||
{{ station.onlineInfo?.scheduledTrains?.filter((train) => train.stopInfo.confirmed).length || '0' }}
|
<span style="color: #ccc">
|
||||||
</span>
|
{{ station.onlineInfo?.scheduledTrains?.filter((train) => !train.stopInfo.confirmed).length || 0 }}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td class="station_schedules" style="width: 30px" :class="{ inactive: !station.onlineInfo }">
|
||||||
|
<span style="color: #66ff6c">
|
||||||
|
{{ station.onlineInfo?.scheduledTrains?.filter((train) => train.stopInfo.confirmed).length || 0 }}
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
@@ -352,9 +368,15 @@ table {
|
|||||||
position: sticky;
|
position: sticky;
|
||||||
top: 0;
|
top: 0;
|
||||||
|
|
||||||
min-width: 80px;
|
&.header-text {
|
||||||
|
min-width: 140px;
|
||||||
|
}
|
||||||
|
|
||||||
padding: 0.5em;
|
&.header-image {
|
||||||
|
min-width: 60px;
|
||||||
|
}
|
||||||
|
|
||||||
|
padding: 0.5em 0.25em;
|
||||||
background-color: $bgCol;
|
background-color: $bgCol;
|
||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
|
|
||||||
|
|||||||
+5
-3
@@ -211,9 +211,11 @@
|
|||||||
"dispatcher-lvl": "Dispatcher\nlevel",
|
"dispatcher-lvl": "Dispatcher\nlevel",
|
||||||
"routes": "Routes\ndouble / single",
|
"routes": "Routes\ndouble / single",
|
||||||
"general": "General info",
|
"general": "General info",
|
||||||
"users": "Drivers online",
|
"user": "Drivers online",
|
||||||
"spawns": "Spawns online",
|
"spawn": "Spawns online",
|
||||||
"timetables": "Active timetables",
|
"timetableAll": "Active timetables",
|
||||||
|
"timetableConfirmed": "Confirmed timetables",
|
||||||
|
"timetableUnconfirmed": "Unconfirmed timetables",
|
||||||
"no-stations": "No stations to show here!",
|
"no-stations": "No stations to show here!",
|
||||||
"scenery-search": "Search for scenery..."
|
"scenery-search": "Search for scenery..."
|
||||||
},
|
},
|
||||||
|
|||||||
+5
-3
@@ -215,9 +215,11 @@
|
|||||||
"dispatcher-lvl": "Poziom\ndyżurnego",
|
"dispatcher-lvl": "Poziom\ndyżurnego",
|
||||||
"routes": "Szlaki\n2tor / 1tor",
|
"routes": "Szlaki\n2tor / 1tor",
|
||||||
"general": "Informacje\nogólne",
|
"general": "Informacje\nogólne",
|
||||||
"users": "Maszyniści online",
|
"user": "Maszyniści online",
|
||||||
"spawns": "Otwarte spawny",
|
"spawn": "Otwarte spawny",
|
||||||
"timetables": "Aktywne rozkłady jazdy",
|
"timetableAll": "Aktywne rozkłady jazdy",
|
||||||
|
"timetableConfirmed": "Zatwierdzone rozkłady jazdy",
|
||||||
|
"timetableUnconfirmed": "Niezatwierdzone rozkłady jazdy",
|
||||||
"no-stations": "Brak stacji do wyświetlenia!",
|
"no-stations": "Brak stacji do wyświetlenia!",
|
||||||
"scenery-search": "Wyszukaj scenerię..."
|
"scenery-search": "Wyszukaj scenerię..."
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,13 +1,5 @@
|
|||||||
export const headIds = [
|
export const headIds = ['station', 'min-lvl', 'status', 'dispatcher', 'dispatcher-lvl', 'routes', 'general'] as const;
|
||||||
'station',
|
|
||||||
'min-lvl',
|
|
||||||
'status',
|
|
||||||
'dispatcher',
|
|
||||||
'dispatcher-lvl',
|
|
||||||
'routes',
|
|
||||||
'general',
|
|
||||||
] as const;
|
|
||||||
|
|
||||||
export const headIconsIds = ['user', 'spawn', 'timetable'] as const;
|
export const headIconsIds = ['user', 'spawn', 'timetableAll', 'timetableUnconfirmed', 'timetableConfirmed'] as const;
|
||||||
|
|
||||||
export type HeadIdsTypes = typeof headIds[number] | typeof headIconsIds[number];
|
export type HeadIdsTypes = typeof headIds[number] | typeof headIconsIds[number];
|
||||||
|
|||||||
@@ -55,6 +55,16 @@ export interface APIData {
|
|||||||
connectedSocketCount: number;
|
connectedSocketCount: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface StationRoutesInfo {
|
||||||
|
routeName: string;
|
||||||
|
isElectric: boolean;
|
||||||
|
isInternal: boolean;
|
||||||
|
isRouteSBL: boolean;
|
||||||
|
routeLength: number;
|
||||||
|
routeSpeed: number;
|
||||||
|
routeTracks: number;
|
||||||
|
}
|
||||||
|
|
||||||
export interface StationJSONData {
|
export interface StationJSONData {
|
||||||
name: string;
|
name: string;
|
||||||
abbr: string;
|
abbr: string;
|
||||||
@@ -70,7 +80,8 @@ export interface StationJSONData {
|
|||||||
|
|
||||||
SUP: boolean;
|
SUP: boolean;
|
||||||
|
|
||||||
routes: string;
|
// routes: string;
|
||||||
|
routesInfo: StationRoutesInfo[];
|
||||||
|
|
||||||
checkpoints: string | null;
|
checkpoints: string | null;
|
||||||
authors?: string;
|
authors?: string;
|
||||||
|
|||||||
+30
-48
@@ -3,7 +3,7 @@ import { defineStore } from 'pinia';
|
|||||||
import { io } from 'socket.io-client';
|
import { io } from 'socket.io-client';
|
||||||
import { DataStatus } from '../scripts/enums/DataStatus';
|
import { DataStatus } from '../scripts/enums/DataStatus';
|
||||||
import StationAPIData from '../scripts/interfaces/api/StationAPIData';
|
import StationAPIData from '../scripts/interfaces/api/StationAPIData';
|
||||||
import {ScheduledTrain} from '../scripts/interfaces/ScheduledTrain';
|
import { ScheduledTrain } from '../scripts/interfaces/ScheduledTrain';
|
||||||
import Station from '../scripts/interfaces/Station';
|
import Station from '../scripts/interfaces/Station';
|
||||||
import StationRoutes from '../scripts/interfaces/StationRoutes';
|
import StationRoutes from '../scripts/interfaces/StationRoutes';
|
||||||
import Train from '../scripts/interfaces/Train';
|
import Train from '../scripts/interfaces/Train';
|
||||||
@@ -303,57 +303,39 @@ export const useStore = defineStore('store', {
|
|||||||
...scenery,
|
...scenery,
|
||||||
authors: scenery.authors?.split(',').map((a) => a.trim()),
|
authors: scenery.authors?.split(',').map((a) => a.trim()),
|
||||||
routes:
|
routes:
|
||||||
scenery.routes
|
scenery.routesInfo.reduce(
|
||||||
?.split(';')
|
(acc, route) => {
|
||||||
.filter((routeString) => routeString)
|
const propName: keyof StationRoutes = `${route.routeTracks == 2 ? 'twoWay' : 'oneWay'}${
|
||||||
.reduce(
|
route.isElectric ? '' : 'No'
|
||||||
(acc, routeString) => {
|
}CatenaryRouteNames`;
|
||||||
const specs1 = routeString.split('_')[0];
|
|
||||||
const isInternal = specs1.startsWith('!');
|
|
||||||
const name = isInternal ? specs1.replace('!', '') : specs1;
|
|
||||||
|
|
||||||
const specs2 = routeString.split('_')[1].split('');
|
acc[route.routeTracks == 2 ? 'twoWay' : 'oneWay'].push({
|
||||||
const twoWay = specs2[0] == '2';
|
name: route.routeName,
|
||||||
const catenary = specs2[1] == 'E';
|
SBL: route.isRouteSBL,
|
||||||
const SBL = specs2[2] == 'S';
|
TWB: false,
|
||||||
const TWB = specs2[3] ? true : false;
|
catenary: route.isElectric,
|
||||||
const speed = Number(routeString.split(':')[1]) || 0;
|
isInternal: route.isInternal,
|
||||||
const length = Number(routeString.split(':')[2]) || 0;
|
tracks: route.routeTracks,
|
||||||
|
length: route.routeLength,
|
||||||
|
speed: route.routeSpeed,
|
||||||
|
});
|
||||||
|
|
||||||
const propName = twoWay
|
if (!route.isInternal) acc[propName].push(route.routeName);
|
||||||
? catenary
|
|
||||||
? 'twoWayCatenaryRouteNames'
|
|
||||||
: 'twoWayNoCatenaryRouteNames'
|
|
||||||
: catenary
|
|
||||||
? 'oneWayCatenaryRouteNames'
|
|
||||||
: 'oneWayNoCatenaryRouteNames';
|
|
||||||
|
|
||||||
acc[twoWay ? 'twoWay' : 'oneWay'].push({
|
if (route.isRouteSBL) acc['sblRouteNames'].push(route.routeName);
|
||||||
name,
|
|
||||||
SBL,
|
|
||||||
TWB,
|
|
||||||
catenary,
|
|
||||||
isInternal,
|
|
||||||
tracks: twoWay ? 2 : 1,
|
|
||||||
length,
|
|
||||||
speed,
|
|
||||||
});
|
|
||||||
if (!isInternal) acc[propName].push(name);
|
|
||||||
|
|
||||||
if (SBL) acc['sblRouteNames'].push(name);
|
return acc;
|
||||||
|
},
|
||||||
return acc;
|
{
|
||||||
},
|
oneWay: [],
|
||||||
{
|
twoWay: [],
|
||||||
oneWay: [],
|
sblRouteNames: [],
|
||||||
twoWay: [],
|
oneWayCatenaryRouteNames: [],
|
||||||
sblRouteNames: [],
|
oneWayNoCatenaryRouteNames: [],
|
||||||
oneWayCatenaryRouteNames: [],
|
twoWayCatenaryRouteNames: [],
|
||||||
oneWayNoCatenaryRouteNames: [],
|
twoWayNoCatenaryRouteNames: [],
|
||||||
twoWayCatenaryRouteNames: [],
|
} as StationRoutes
|
||||||
twoWayNoCatenaryRouteNames: [],
|
) || {},
|
||||||
} as StationRoutes
|
|
||||||
) || {},
|
|
||||||
checkpoints: scenery.checkpoints
|
checkpoints: scenery.checkpoints
|
||||||
? scenery.checkpoints.split(';').map((sub) => ({ checkpointName: sub, scheduledTrains: [] }))
|
? scenery.checkpoints.split(';').map((sub) => ({ checkpointName: sub, scheduledTrains: [] }))
|
||||||
: [],
|
: [],
|
||||||
|
|||||||
@@ -3,18 +3,18 @@ import Filter from '../../scripts/interfaces/Filter';
|
|||||||
import Station from '../../scripts/interfaces/Station';
|
import Station from '../../scripts/interfaces/Station';
|
||||||
|
|
||||||
export const sortStations = (a: Station, b: Station, sorter: { headerName: HeadIdsTypes; dir: number }) => {
|
export const sortStations = (a: Station, b: Station, sorter: { headerName: HeadIdsTypes; dir: number }) => {
|
||||||
|
let diff = 0;
|
||||||
|
|
||||||
switch (sorter.headerName) {
|
switch (sorter.headerName) {
|
||||||
case 'station':
|
case 'station':
|
||||||
return sorter.dir == 1 ? a.name.localeCompare(b.name) : b.name.localeCompare(a.name);
|
return sorter.dir == 1 ? a.name.localeCompare(b.name) : b.name.localeCompare(a.name);
|
||||||
|
|
||||||
case 'min-lvl':
|
case 'min-lvl':
|
||||||
if ((a.generalInfo?.reqLevel || 0) > (b.generalInfo?.reqLevel || 0)) return sorter.dir;
|
diff = (a.generalInfo?.reqLevel || 0) - (b.generalInfo?.reqLevel || 0);
|
||||||
if ((a.generalInfo?.reqLevel || 0) < (b.generalInfo?.reqLevel || 0)) return -sorter.dir;
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'status':
|
case 'status':
|
||||||
if ((a.onlineInfo?.statusTimestamp || 0) > (b.onlineInfo?.statusTimestamp || 0)) return sorter.dir;
|
diff = (a.onlineInfo?.statusTimestamp || 0) - (b.onlineInfo?.statusTimestamp || 0);
|
||||||
if ((a.onlineInfo?.statusTimestamp || 0) < (b.onlineInfo?.statusTimestamp || 0)) return -sorter.dir;
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'dispatcher':
|
case 'dispatcher':
|
||||||
@@ -25,34 +25,48 @@ export const sortStations = (a: Station, b: Station, sorter: { headerName: HeadI
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case 'dispatcher-lvl':
|
case 'dispatcher-lvl':
|
||||||
if ((a.onlineInfo?.dispatcherExp || 0) > (b.onlineInfo?.dispatcherExp || 0)) return sorter.dir;
|
diff = (a.onlineInfo?.dispatcherExp || 0) - (b.onlineInfo?.dispatcherExp || 0);
|
||||||
if ((a.onlineInfo?.dispatcherExp || 0) < (b.onlineInfo?.dispatcherExp || 0)) return -sorter.dir;
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'user':
|
case 'user':
|
||||||
if ((a.onlineInfo?.currentUsers || 0) > (b.onlineInfo?.currentUsers || 0)) return sorter.dir;
|
diff = (b.onlineInfo ? b.onlineInfo.currentUsers : -1) - (a.onlineInfo ? a.onlineInfo.currentUsers : -1);
|
||||||
if ((a.onlineInfo?.currentUsers || 0) < (b.onlineInfo?.currentUsers || 0)) return -sorter.dir;
|
|
||||||
|
|
||||||
if ((a.onlineInfo?.maxUsers || 0) > (b.onlineInfo?.maxUsers || 0)) return sorter.dir;
|
|
||||||
if ((a.onlineInfo?.maxUsers || 0) < (b.onlineInfo?.maxUsers || 0)) return -sorter.dir;
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'spawn':
|
case 'spawn':
|
||||||
if ((a.onlineInfo?.spawns.length || 0) > (b.onlineInfo?.spawns.length || 0)) return sorter.dir;
|
diff = (a.onlineInfo ? a.onlineInfo.spawns.length : -1) - (b.onlineInfo ? b.onlineInfo.spawns.length : -1);
|
||||||
if ((a.onlineInfo?.spawns.length || 0) < (b.onlineInfo?.spawns.length || 0)) return -sorter.dir;
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'timetable':
|
case 'timetableConfirmed':
|
||||||
if ((a.onlineInfo?.scheduledTrains?.length || 0) > (b.onlineInfo?.scheduledTrains?.length || 0))
|
diff =
|
||||||
return sorter.dir;
|
(a.onlineInfo?.scheduledTrains
|
||||||
if ((a.onlineInfo?.scheduledTrains?.length || 0) < (b.onlineInfo?.scheduledTrains?.length || 0))
|
? a.onlineInfo.scheduledTrains.filter((train) => train.stopInfo.confirmed).length
|
||||||
return -sorter.dir;
|
: -1) -
|
||||||
|
(b.onlineInfo?.scheduledTrains
|
||||||
|
? b.onlineInfo.scheduledTrains.filter((train) => train.stopInfo.confirmed).length
|
||||||
|
: -1);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'timetableUnconfirmed':
|
||||||
|
diff =
|
||||||
|
(a.onlineInfo?.scheduledTrains
|
||||||
|
? a.onlineInfo.scheduledTrains.filter((train) => !train.stopInfo.confirmed).length
|
||||||
|
: -1) -
|
||||||
|
(b.onlineInfo?.scheduledTrains
|
||||||
|
? b.onlineInfo.scheduledTrains.filter((train) => !train.stopInfo.confirmed).length
|
||||||
|
: -1);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'timetableAll':
|
||||||
|
diff =
|
||||||
|
(a.onlineInfo?.scheduledTrains ? a.onlineInfo.scheduledTrains.length : -1) -
|
||||||
|
(b.onlineInfo?.scheduledTrains ? b.onlineInfo.scheduledTrains.length : -1);
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (diff != 0) return Math.sign(diff) * sorter.dir;
|
||||||
return a.name.localeCompare(b.name);
|
return a.name.localeCompare(b.name);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user