|
32 | 32 | </div> |
33 | 33 |
|
34 | 34 | <div class="page-body"> |
| 35 | + <div class="sites-mobile-list"> |
| 36 | + <article |
| 37 | + v-for="site in filteredSites" |
| 38 | + :key="site.domain" |
| 39 | + class="site-mobile-card" |
| 40 | + > |
| 41 | + <div class="site-mobile-main" @click="editSite(site)"> |
| 42 | + <div class="site-mobile-title-row"> |
| 43 | + <span class="site-mobile-title">{{ site.domain }}</span> |
| 44 | + <el-tag |
| 45 | + v-if="site.sslEnabled" |
| 46 | + size="small" |
| 47 | + type="success" |
| 48 | + effect="dark" |
| 49 | + class="cell-tag" |
| 50 | + >SSL</el-tag> |
| 51 | + </div> |
| 52 | + <div class="site-mobile-meta mono" :title="site.documentRoot"> |
| 53 | + {{ site.documentRoot }} |
| 54 | + </div> |
| 55 | + <div class="site-mobile-badges"> |
| 56 | + <el-tag |
| 57 | + v-if="rowRuntimeLabel(site)" |
| 58 | + size="small" |
| 59 | + effect="dark" |
| 60 | + class="runtime-tag runtime-php" |
| 61 | + >{{ rowRuntimeLabel(site) }}</el-tag> |
| 62 | + <span v-if="site.aliases?.length" class="alias-dot">+{{ site.aliases.length }}</span> |
| 63 | + <span v-if="site.aliases?.length" class="site-mobile-alias mono"> |
| 64 | + {{ site.aliases[0] }} |
| 65 | + </span> |
| 66 | + </div> |
| 67 | + </div> |
| 68 | + <div class="site-mobile-actions"> |
| 69 | + <el-switch |
| 70 | + :model-value="site.enabled !== false" |
| 71 | + :loading="togglingEnabled === site.domain" |
| 72 | + size="small" |
| 73 | + inline-prompt |
| 74 | + :title="site.enabled !== false ? 'Povoleno' : 'Zakázáno'" |
| 75 | + @change="(v: boolean | string | number) => toggleSiteEnabled(site, v)" |
| 76 | + @click.stop |
| 77 | + /> |
| 78 | + <el-button size="small" type="primary" @click.stop="editSite(site)">{{ $t('common.edit') }}</el-button> |
| 79 | + <el-dropdown |
| 80 | + trigger="click" |
| 81 | + :teleported="true" |
| 82 | + :popper-options="{ modifiers: [{ name: 'preventOverflow', options: { boundary: 'viewport', padding: 8 } }] }" |
| 83 | + @command="(cmd: string) => handleRowAction(cmd, site)" |
| 84 | + > |
| 85 | + <el-button size="small" circle :aria-label="$t('sites.card.moreActions', { domain: site.domain })"><el-icon><MoreFilled /></el-icon></el-button> |
| 86 | + <template #dropdown> |
| 87 | + <el-dropdown-menu> |
| 88 | + <el-dropdown-item command="open"> |
| 89 | + {{ $t('sites.open') }} |
| 90 | + </el-dropdown-item> |
| 91 | + <el-dropdown-item command="detect"> |
| 92 | + <el-icon><RefreshRight /></el-icon> {{ $t('sites.detect') }} |
| 93 | + </el-dropdown-item> |
| 94 | + <el-dropdown-item command="delete" divided class="danger-item">{{ $t('sites.delete') }}</el-dropdown-item> |
| 95 | + </el-dropdown-menu> |
| 96 | + </template> |
| 97 | + </el-dropdown> |
| 98 | + </div> |
| 99 | + </article> |
| 100 | + </div> |
| 101 | + |
35 | 102 | <el-table |
36 | 103 | :data="filteredSites" |
37 | 104 | v-loading="sitesStore.loading" |
@@ -304,7 +371,7 @@ import { usePluginsStore } from '../../stores/plugins' |
304 | 371 | import type { SiteInfo } from '../../api/types' |
305 | 372 | import { fetchDockerComposeStatus, fetchPhpVersions, fetchSystem, daemonBaseUrl, daemonAuthHeaders as authHeaders, type DockerComposeStatus } from '../../api/daemon' |
306 | 373 | import { errorMessage } from '../../utils/errors' |
307 | | -import { MoreFilled } from '@element-plus/icons-vue' |
| 374 | +import { MoreFilled, RefreshRight } from '@element-plus/icons-vue' |
308 | 375 | import SitesListSimple from './SitesListSimple.vue' |
309 | 376 | import PluginSlot from '../shared/PluginSlot.vue' |
310 | 377 | import siteTemplatesConfig from '../../config/site-templates.json' |
@@ -384,6 +451,12 @@ function phpFullLabel(stored: string): string { |
384 | 451 | const match = phpVersions.value.find(p => p.value === stored) |
385 | 452 | return match ? match.label : `PHP ${stored}` |
386 | 453 | } |
| 454 | +
|
| 455 | +function rowRuntimeLabel(site: SiteInfo): string { |
| 456 | + if (site.nodeUpstreamPort && site.nodeUpstreamPort > 0) return `Node:${site.nodeUpstreamPort}` |
| 457 | + if (site.phpVersion && site.phpVersion !== 'none') return phpFullLabel(site.phpVersion) |
| 458 | + return 'Static' |
| 459 | +} |
387 | 460 | // Native-looking Document Root placeholder. Windows keeps the legacy |
388 | 461 | // C:\work\htdocs\<app> hint; macOS/Linux get a path rooted at the user's |
389 | 462 | // home dir so the placeholder itself isn't misleading on those OSes. |
@@ -778,6 +851,10 @@ function handleRowAction(cmd: string, row: SiteInfo) { |
778 | 851 | padding: 0 24px 24px; |
779 | 852 | } |
780 | 853 |
|
| 854 | +.sites-mobile-list { |
| 855 | + display: none; |
| 856 | +} |
| 857 | +
|
781 | 858 | .sites-table :deep(.el-table__header) { |
782 | 859 | background: var(--wdc-surface-2); |
783 | 860 | } |
@@ -953,6 +1030,117 @@ function handleRowAction(cmd: string, row: SiteInfo) { |
953 | 1030 | } |
954 | 1031 | } |
955 | 1032 |
|
| 1033 | +@media (max-width: 700px) { |
| 1034 | + .page-header { |
| 1035 | + align-items: flex-start; |
| 1036 | + flex-direction: column; |
| 1037 | + gap: 12px; |
| 1038 | + padding: 18px 16px 0; |
| 1039 | + margin-bottom: 14px; |
| 1040 | + } |
| 1041 | +
|
| 1042 | + .header-actions { |
| 1043 | + display: grid; |
| 1044 | + grid-template-columns: 1fr 1fr; |
| 1045 | + gap: 8px; |
| 1046 | + width: 100%; |
| 1047 | + } |
| 1048 | +
|
| 1049 | + .header-actions :deep(.el-button) { |
| 1050 | + width: 100%; |
| 1051 | + margin-left: 0 !important; |
| 1052 | + } |
| 1053 | +
|
| 1054 | + .header-actions :deep(.el-button--primary) { |
| 1055 | + grid-column: 1 / -1; |
| 1056 | + } |
| 1057 | +
|
| 1058 | + .search-bar, |
| 1059 | + .page-body { |
| 1060 | + padding-left: 16px; |
| 1061 | + padding-right: 16px; |
| 1062 | + } |
| 1063 | +
|
| 1064 | + .search-bar :deep(.el-input) { |
| 1065 | + max-width: none !important; |
| 1066 | + width: 100%; |
| 1067 | + } |
| 1068 | +
|
| 1069 | + .sites-table { |
| 1070 | + display: none; |
| 1071 | + } |
| 1072 | +
|
| 1073 | + .sites-mobile-list { |
| 1074 | + display: grid; |
| 1075 | + gap: 10px; |
| 1076 | + } |
| 1077 | +
|
| 1078 | + .site-mobile-card { |
| 1079 | + display: grid; |
| 1080 | + grid-template-columns: minmax(0, 1fr) auto; |
| 1081 | + gap: 12px; |
| 1082 | + align-items: center; |
| 1083 | + min-width: 0; |
| 1084 | + padding: 12px; |
| 1085 | + background: var(--wdc-surface); |
| 1086 | + border: 1px solid var(--wdc-border); |
| 1087 | + border-radius: 8px; |
| 1088 | + } |
| 1089 | +
|
| 1090 | + .site-mobile-main { |
| 1091 | + min-width: 0; |
| 1092 | + cursor: pointer; |
| 1093 | + } |
| 1094 | +
|
| 1095 | + .site-mobile-title-row, |
| 1096 | + .site-mobile-badges, |
| 1097 | + .site-mobile-actions { |
| 1098 | + display: flex; |
| 1099 | + align-items: center; |
| 1100 | + gap: 6px; |
| 1101 | + min-width: 0; |
| 1102 | + } |
| 1103 | +
|
| 1104 | + .site-mobile-title { |
| 1105 | + min-width: 0; |
| 1106 | + overflow: hidden; |
| 1107 | + text-overflow: ellipsis; |
| 1108 | + white-space: nowrap; |
| 1109 | + color: var(--wdc-text); |
| 1110 | + font-size: 0.96rem; |
| 1111 | + font-weight: 700; |
| 1112 | + } |
| 1113 | +
|
| 1114 | + .site-mobile-meta { |
| 1115 | + margin-top: 4px; |
| 1116 | + color: var(--wdc-text-3); |
| 1117 | + font-size: 0.72rem; |
| 1118 | + overflow: hidden; |
| 1119 | + text-overflow: ellipsis; |
| 1120 | + white-space: nowrap; |
| 1121 | + } |
| 1122 | +
|
| 1123 | + .site-mobile-badges { |
| 1124 | + margin-top: 8px; |
| 1125 | + flex-wrap: wrap; |
| 1126 | + } |
| 1127 | +
|
| 1128 | + .site-mobile-alias { |
| 1129 | + min-width: 0; |
| 1130 | + max-width: 150px; |
| 1131 | + overflow: hidden; |
| 1132 | + text-overflow: ellipsis; |
| 1133 | + white-space: nowrap; |
| 1134 | + color: var(--wdc-text-2); |
| 1135 | + font-size: 0.72rem; |
| 1136 | + } |
| 1137 | +
|
| 1138 | + .site-mobile-actions { |
| 1139 | + justify-content: flex-end; |
| 1140 | + flex-wrap: nowrap; |
| 1141 | + } |
| 1142 | +} |
| 1143 | +
|
956 | 1144 | /* Row actions: keep switch + edit + overflow on one line, prevent |
957 | 1145 | wrapping into 2 rows which collides with the fixed-right column |
958 | 1146 | border. */ |
|
0 commit comments