wip: add new record
This commit is contained in:
parent
e15ad04c64
commit
960942c47f
4 changed files with 294 additions and 162 deletions
|
@ -12,11 +12,33 @@ function apiGet(url) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function apiPost(url, data) {
|
||||||
|
return fetch(`${baseUrl}/${url}`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify(data),
|
||||||
|
})
|
||||||
|
.then(res => {
|
||||||
|
if (!res.ok) {
|
||||||
|
// do something here
|
||||||
|
throw new Error('Not ok');
|
||||||
|
}
|
||||||
|
return res.json();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
function getRecords(zone) {
|
function getRecords(zone) {
|
||||||
return apiGet(`zones/${zone}/records`);
|
return apiGet(`zones/${zone}/records`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function createRecords(zone, record) {
|
||||||
|
return apiPost(`zones/${zone}/records`, record);
|
||||||
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
getRecords,
|
getRecords,
|
||||||
|
createRecords,
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,29 +1,84 @@
|
||||||
import { html, render, useState, useEffect } from './vendor/preact/standalone.js';
|
import { html, render, useState, useEffect } from './vendor/preact/standalone.js';
|
||||||
|
|
||||||
import { getRecords } from './api.js';
|
import { getRecords, createRecords } from './api.js';
|
||||||
|
|
||||||
|
|
||||||
const rdataInputProperties = {
|
const rdataInputProperties = {
|
||||||
Address: {label: 'adresse :', type: 'text'},
|
Address: {label: 'Adresse', type: 'text'},
|
||||||
Serial: {label: 'serial :', type: 'number'},
|
Serial: {label: 'Numéro de série', type: 'number'},
|
||||||
Minimum: {label: 'minimum :', type: 'number'},
|
Minimum: {label: 'Minimum', type: 'number'},
|
||||||
Retry: {label: 'nouvelle tentative :', type: 'number'},
|
Retry: {label: 'Nouvelle tentative', type: 'number'},
|
||||||
Refresh: {label: 'actualisation :', type: 'number'},
|
Refresh: {label: 'Actualisation', type: 'number'},
|
||||||
MaintainerName: {label: 'contact :', type: 'text'},
|
MaintainerName: {label: 'Contact', type: 'text'},
|
||||||
MasterServerName: {label: 'serveur primaire :', type: 'text'},
|
MasterServerName: {label: 'Serveur primaire', type: 'text'},
|
||||||
Expire: {label: 'expiration :', type: 'number'},
|
Expire: {label: 'Expiration', type: 'number'},
|
||||||
Target: {label: 'cible :', type: 'text'},
|
Target: {label: 'Cible', type: 'text'},
|
||||||
};
|
};
|
||||||
|
|
||||||
const recordTypes = {
|
const realRecordDataConfig = {
|
||||||
'A': 'address',
|
'A': {
|
||||||
'AAAA': 'address',
|
friendlyType: 'address',
|
||||||
'SRV': 'service',
|
fields: ['Address'],
|
||||||
'CNAME': 'alias',
|
},
|
||||||
'NS': 'name_server',
|
'AAAA': {
|
||||||
'SOA': 'soa',
|
friendlyType: 'address',
|
||||||
|
fields: ['Address'],
|
||||||
|
},
|
||||||
|
'CNAME': {
|
||||||
|
friendlyType: 'alias',
|
||||||
|
fields: ['Target'],
|
||||||
|
},
|
||||||
|
'SRV': {
|
||||||
|
friendlyType: 'service',
|
||||||
|
fields: [ /* TODO */ ],
|
||||||
|
},
|
||||||
|
'NS': {
|
||||||
|
friendlyType: 'name_server',
|
||||||
|
fields: ['Target'],
|
||||||
|
},
|
||||||
|
'SOA': {
|
||||||
|
friendlyType: 'soa',
|
||||||
|
fields: ['MasterServerName', 'MaintainerName', 'Refresh', 'Retry', 'Expire', 'Minimum', 'Serial'],
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function defaultBuildData(realRecordType) {
|
||||||
|
const defaultFields = realRecordDataConfig[realRecordType].fields.map(field => [field, null]);
|
||||||
|
return (fields) => {
|
||||||
|
return {...defaultFields, ...fields, Type: realRecordType};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function defaultRecordToField(realRecord) {
|
||||||
|
const type = realRecord.Type;
|
||||||
|
return realRecordDataConfig[type].fields.map(field => [field, realRecord[field]]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const friendlyRecordDataConfig = {
|
||||||
|
'address': {
|
||||||
|
realRecordToFields: defaultRecordToField,
|
||||||
|
fields: realRecordDataConfig['AAAA'].fields,
|
||||||
|
buildData: buildAddressRecord,
|
||||||
|
},
|
||||||
|
'alias': {
|
||||||
|
realRecordToFields: defaultRecordToField,
|
||||||
|
fields: realRecordDataConfig['CNAME'].fields,
|
||||||
|
buildData: defaultBuildData('CNAME'),
|
||||||
|
},
|
||||||
|
'name_server': {
|
||||||
|
realRecordToFields: defaultRecordToField,
|
||||||
|
fields: realRecordDataConfig['CNAME'].fields,
|
||||||
|
buildData: defaultBuildData('NS'),
|
||||||
|
},
|
||||||
|
'soa': {
|
||||||
|
realRecordToFields: defaultRecordToField,
|
||||||
|
fields: realRecordDataConfig['SOA'].fields,
|
||||||
|
buildData: defaultBuildData('SOA'),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
const recordTypeNames = {
|
const recordTypeNames = {
|
||||||
'address': 'Adresse IP',
|
'address': 'Adresse IP',
|
||||||
'service': 'Service',
|
'service': 'Service',
|
||||||
|
@ -32,18 +87,20 @@ const recordTypeNames = {
|
||||||
'soa': 'SOA',
|
'soa': 'SOA',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Type to use for SRV to derive name without port / service */
|
||||||
function getNameForRecord(name, type) {
|
function getNameForRecord(name, type) {
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getTypeForRecord(name, type) {
|
/* Name to use with _spf for example */
|
||||||
return recordTypes[type];
|
function getFriendlyTypeForRecord(name, type) {
|
||||||
|
return realRecordDataConfig[type].friendlyType;
|
||||||
}
|
}
|
||||||
|
|
||||||
function processRecords(records) {
|
function processRecords(records) {
|
||||||
return records.reduce((acc, record) => {
|
return records.reduce((acc, record) => {
|
||||||
let name = getNameForRecord(record.Name, record.Type);
|
let name = getNameForRecord(record.Name, record.Type);
|
||||||
let type = getTypeForRecord(record.Name, record.Type);
|
let type = getFriendlyTypeForRecord(record.Name, record.Type);
|
||||||
if (!(name in acc)) {
|
if (!(name in acc)) {
|
||||||
acc[name] = {};
|
acc[name] = {};
|
||||||
}
|
}
|
||||||
|
@ -54,26 +111,16 @@ function processRecords(records) {
|
||||||
return acc;
|
return acc;
|
||||||
}, {});
|
}, {});
|
||||||
}
|
}
|
||||||
const recordsKeys = {
|
|
||||||
'address': (record) => { return [[ 'Address', record.Address ]] },
|
function buildAddressRecord({ Address = ''}) {
|
||||||
'service': (record) => { /* TODO */ },
|
return {
|
||||||
'alias': (record) => { return [[ 'Target', record.Target ]] },
|
Type: Address.indexOf(':') > -1 ? 'AAAA' : 'A',
|
||||||
'name_server': (record) => { return [[ 'Target', record.Target ]] },
|
Address
|
||||||
'soa': (record) => {
|
}
|
||||||
return [
|
|
||||||
[ 'MasterServerName', record.MasterServerName ],
|
|
||||||
[ 'MaintainerName', record.MaintainerName ],
|
|
||||||
[ 'Refresh', record.Refresh ],
|
|
||||||
[ 'Retry', record.Retry ],
|
|
||||||
[ 'Expire', record.Expire ],
|
|
||||||
[ 'Minimum', record.Minimum ],
|
|
||||||
[ 'Serial', record.Serial ],
|
|
||||||
]
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function FriendlyRecord({type, record}) {
|
function FriendlyRecord({type, record}) {
|
||||||
let keys = recordsKeys[type](record);
|
let keys = friendlyRecordDataConfig[type].realRecordToFields(record);
|
||||||
if (keys.length == 1) {
|
if (keys.length == 1) {
|
||||||
return html`<span>${keys[0][1]}</span>`;
|
return html`<span>${keys[0][1]}</span>`;
|
||||||
} else {
|
} else {
|
||||||
|
@ -122,7 +169,6 @@ function RecordListFriendly({ zone }) {
|
||||||
${Object.entries(records).map(
|
${Object.entries(records).map(
|
||||||
([name, recordSets]) => {
|
([name, recordSets]) => {
|
||||||
return html`
|
return html`
|
||||||
|
|
||||||
<${RecordsByName} name=${name} recordSets=${recordSets}/>
|
<${RecordsByName} name=${name} recordSets=${recordSets}/>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
@ -130,46 +176,96 @@ function RecordListFriendly({ zone }) {
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function NewRecordFormFriendly({ zone }) {
|
function NewRecordFormFriendly({ zone, enabled, id, onCancel }) {
|
||||||
|
const [recordType, setRecordType] = useState(Object.keys(recordTypeNames)[0]);
|
||||||
|
const [recordData, setRecordData] = useState({});
|
||||||
|
const [realRecordData, setRealRecordData] = useState({});
|
||||||
|
const [realType, setRealType] = useState('');
|
||||||
|
const [domain, setDomain] = useState('');
|
||||||
|
const [ttl, setTTL] = useState(3600);
|
||||||
|
|
||||||
|
const setRecordDataFactory = (field) => {
|
||||||
|
return (e) => {
|
||||||
|
const newData = {...recordData};
|
||||||
|
newData[field] = e.target.value;
|
||||||
|
const newRealRecordData = friendlyRecordDataConfig[recordType].buildData(newData)
|
||||||
|
setRecordData(newData);
|
||||||
|
setRealRecordData(newRealRecordData);
|
||||||
|
setRealType(newRealRecordData.Type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const createNewRecord = (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
const newRecords = [{...realRecordData, Class: 'IN', TTL: ttl, Name: `${domain}.${zone}`}];
|
||||||
|
console.log(newRecords)
|
||||||
|
createRecords(zone, newRecords);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Reset valeurs champs quand changement de type => bound la valeur de l'input au state
|
||||||
|
// TODO: Dans le cas où un domain est dans le RDATA mettre le domaine absolue dans la preview
|
||||||
|
// TODO: Déplacer preview dans son component, faire une vue en "diff" et l'appeler "prévisualisation des changements"
|
||||||
|
// TODO: Validation des données client et serveur
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<form class="new-record">
|
<form class="new-record ${enabled ? '' : 'disabled'}" id=${id}>
|
||||||
<div>
|
<h3>Nouvel enregistrement</h3>
|
||||||
<label for="domain">Domaine</label>
|
<div class="form-row">
|
||||||
|
<div class="input-group">
|
||||||
|
<label for="domain">Domaine</label>
|
||||||
|
<div class="combined-input">
|
||||||
|
<input type="text" id="domain" name="domain" onChange=${e => setDomain(e.target.value)}/>
|
||||||
|
<span>.${ zone }</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="input-group">
|
||||||
|
<label for="record_type">Type d'enregistrement</label>
|
||||||
|
<select id="record_type" name="record_type" onChange=${e => { setRecordType(e.target.value); setRealType(''); setRecordData({}); setRealRecordData({})}}>
|
||||||
|
${Object.entries(recordTypeNames).map(([type, name]) => html`<option value="${type}">${name}</option>`)}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<input type="text" id="domain"/>
|
${friendlyRecordDataConfig[recordType].fields.map(fieldName => html`
|
||||||
<span>.${ zone }</span>
|
<div class="input-group">
|
||||||
|
<label for="${fieldName}">${rdataInputProperties[fieldName].label}</label>
|
||||||
|
<input id="${fieldName}" type="${rdataInputProperties[fieldName].type}" onChange=${setRecordDataFactory(fieldName)}></input>
|
||||||
|
</div>
|
||||||
|
`)}
|
||||||
|
</div>
|
||||||
|
<div class="input-group">
|
||||||
|
<label for="ttl">TTL</label>
|
||||||
|
<input type="number" name="ttl" id="ttl" value="${ttl}" onChange=${e => setTTL(e.target.value)}/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<article class="preview">
|
||||||
|
<h4>Prévisualisation de l'enregistrement</h3>
|
||||||
|
<pre>
|
||||||
|
${domain == '' ? '@' : domain + '.' + zone} ${ttl} IN ${realType} ${realType != '' ? realRecordDataConfig[realType].fields.map(field => realRecordData[field]).join(' ') : ''}
|
||||||
|
</pre>
|
||||||
|
</article>
|
||||||
<div>
|
<div>
|
||||||
<label for="record_type">Type d'enregistrement</label>
|
<input type="submit" onClick=${createNewRecord} value="Ajouter"/>
|
||||||
<select id="record_type">
|
<button role="button" onClick=${onCancel}>Anuler</button>
|
||||||
<option>Adresse IP</option>
|
|
||||||
<option>Alias</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<input type="text"/>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<input type="submit" value="Enregistrer"/>
|
|
||||||
<button role="button">Anuler</button>
|
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function ZoneRecords({ zone }) {
|
function ZoneRecords({ zone }) {
|
||||||
const [addNewRecord, setAddNewRecord] = useState(false);
|
const [addNewRecord, setAddNewRecord] = useState(true);
|
||||||
const [newRecords, setNewRecords] = useState([]);
|
|
||||||
|
const onCancelNewRecord = (e) => { setAddNewRecord(false); e.preventDefault() };
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<header>
|
<header>
|
||||||
<h2>Enregistrements</h2>
|
<h2>Enregistrements</h2>
|
||||||
<button onClick=${() => setNewRecords([...newRecords, NewRecordFormFriendly({ zone })])}>Ajouter un enregistrement</button>
|
<button onClick=${() => setAddNewRecord(true)} aria-controls="add-new-record-form" aria-expanded=${addNewRecord} disabled=${addNewRecord}>Ajouter un enregistrement</button>
|
||||||
<button>Éditer la zone</button>
|
<button>Éditer la zone</button>
|
||||||
</header>
|
</header>
|
||||||
${newRecords}
|
<${NewRecordFormFriendly} zone=${zone} enabled=${addNewRecord} id="add-new-record-form" onCancel=${onCancelNewRecord}/>
|
||||||
<${RecordListFriendly} zone=${zone} />
|
<div class="zone-content">
|
||||||
|
<${RecordListFriendly} zone=${zone} />
|
||||||
|
</div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,12 +4,13 @@ body {
|
||||||
min-width: 100vw;
|
min-width: 100vw;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
font-family: sans-serif;
|
font-family: sans-serif;
|
||||||
|
line-height: 1.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
--color-primary: #5e0c97;
|
--color-primary: 94, 12, 151;
|
||||||
--color-hightlight-1: #ffd4ba;
|
--color-hightlight-1: 255, 212, 186;
|
||||||
--color-hightlight-2: #dd39dd;
|
--color-hightlight-2: 208, 44, 167;
|
||||||
--color-contrast: white;
|
--color-contrast: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,7 +27,7 @@ h1 {
|
||||||
}
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
color: var(--color-primary);
|
color: rgb(var(--color-primary));
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
position: relative;
|
position: relative;
|
||||||
padding-bottom: .3em;
|
padding-bottom: .3em;
|
||||||
|
@ -36,18 +37,13 @@ a::after {
|
||||||
content: "";
|
content: "";
|
||||||
display: block;
|
display: block;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
border-bottom: 2px solid var(--color-hightlight-2);
|
border-bottom: 2px solid rgb(var(--color-primary));
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: .1em;
|
bottom: .1em;
|
||||||
transition: .2s;
|
transition: .2s;
|
||||||
}
|
}
|
||||||
|
|
||||||
a:hover::after {
|
a:hover::after {
|
||||||
content: "";
|
|
||||||
display: block;
|
|
||||||
width: 100%;
|
|
||||||
border-bottom: 2px solid var(--color-hightlight-2);
|
|
||||||
position: absolute;
|
|
||||||
bottom: .3em;
|
bottom: .3em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,6 +57,14 @@ p.feedback.error {
|
||||||
color: #710000;
|
color: #710000;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
select,
|
||||||
|
input {
|
||||||
|
border: 1px solid rgb(var(--color-primary));;
|
||||||
|
border-radius: 0;
|
||||||
|
background: var(--color-contrast);
|
||||||
|
}
|
||||||
|
|
||||||
|
select,
|
||||||
button,
|
button,
|
||||||
input {
|
input {
|
||||||
padding: .35rem .35rem;
|
padding: .35rem .35rem;
|
||||||
|
@ -69,25 +73,35 @@ input {
|
||||||
|
|
||||||
button,
|
button,
|
||||||
input[type="submit"] {
|
input[type="submit"] {
|
||||||
background: var(--color-primary);
|
background: rgb(var(--color-primary));
|
||||||
color: var(--color-contrast);
|
color: var(--color-contrast);
|
||||||
border-left: 5px solid var(--color-hightlight-1);
|
border-left: 5px solid rgb(var(--color-hightlight-1));
|
||||||
border-top: 5px solid var(--color-hightlight-1);
|
border-top: 5px solid rgb(var(--color-hightlight-1));
|
||||||
border-right: 5px solid var(--color-hightlight-2);
|
border-right: 5px solid rgb(var(--color-hightlight-2));
|
||||||
border-bottom: 5px solid var(--color-hightlight-2);
|
border-bottom: 5px solid rgb(var(--color-hightlight-2));
|
||||||
}
|
}
|
||||||
|
|
||||||
button:hover,
|
button:hover:not([disabled]),
|
||||||
input[type="submit"]:hover {
|
input[type="submit"]:hover:not([disabled]) {
|
||||||
background: #7a43a1;
|
background: rgba(var(--color-primary), .8);
|
||||||
}
|
}
|
||||||
|
|
||||||
button:active ,
|
button:active:not([disabled]) ,
|
||||||
input[type="submit"]:active {
|
input[type="submit"]:active:not([disabled]) {
|
||||||
border-left: 5px solid var(--color-hightlight-2);
|
border-left: 5px solid var(--color-hightlight-2);
|
||||||
border-top: 5px solid var(--color-hightlight-2);
|
border-top: 5px solid var(--color-hightlight-2);
|
||||||
border-right: 5px solid var(--color-hightlight-1);
|
border-right: 5px solid rgb(var(--color-hightlight-1));
|
||||||
border-bottom: 5px solid var(--color-hightlight-1);
|
border-bottom: 5px solid rgb(var(--color-hightlight-1));
|
||||||
|
}
|
||||||
|
|
||||||
|
button[disabled],
|
||||||
|
input[type="submit"][disabled] {
|
||||||
|
background: rgba(var(--color-primary), .75);
|
||||||
|
border-left-color: rgba(var(--color-hightlight-1), .75);
|
||||||
|
border-top-color: rgba(var(--color-hightlight-1), .75);
|
||||||
|
border-right-color: rgba(var(--color-hightlight-2), .75);
|
||||||
|
border-bottom-color: rgba(var(--color-hightlight-2), .75);
|
||||||
|
cursor: not-allowed;
|
||||||
}
|
}
|
||||||
|
|
||||||
form input[type="submit"] {
|
form input[type="submit"] {
|
||||||
|
@ -105,18 +119,23 @@ form {
|
||||||
}
|
}
|
||||||
|
|
||||||
nav.main {
|
nav.main {
|
||||||
background: var(--color-primary);
|
background: rgb(var(--color-primary));
|
||||||
min-width: 25ch;
|
min-width: 25ch;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex: 0;
|
flex: 0;
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
border-right: 5px solid var(--color-hightlight-2);
|
border-right: 5px solid rgb(var(--color-hightlight-2));
|
||||||
}
|
}
|
||||||
|
|
||||||
nav.main a {
|
nav.main a {
|
||||||
color: var(--color-contrast);
|
color: var(--color-contrast);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nav.main a::after {
|
||||||
|
border-bottom: 2px solid var(--color-contrast);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
nav.main a img {
|
nav.main a img {
|
||||||
filter: invert(100%);
|
filter: invert(100%);
|
||||||
width: 1.4em;
|
width: 1.4em;
|
||||||
|
@ -137,53 +156,3 @@ nav.main ul li {
|
||||||
nav.main ul ul {
|
nav.main ul ul {
|
||||||
margin-left: 1rem;
|
margin-left: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
zone-content table {
|
|
||||||
border-collapse: collapse;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
zone-content .rdata {
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
zone-content th, .zone-content td {
|
|
||||||
font-weight: normal;
|
|
||||||
text-align: left;
|
|
||||||
vertical-align: top;
|
|
||||||
padding: 0.25rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
zone-content thead {
|
|
||||||
background: #ccb9ff;
|
|
||||||
color: #39004d;
|
|
||||||
}
|
|
||||||
|
|
||||||
zone-content tbody tr:nth-child(even) td {
|
|
||||||
background: #ccb9ff3d;
|
|
||||||
}
|
|
||||||
|
|
||||||
zone-content tbody tr .rdata dt,
|
|
||||||
zone-content tbody tr .rdata label {
|
|
||||||
display: inline-block;
|
|
||||||
padding: 0.1em 0.5em;
|
|
||||||
background: #cecece;
|
|
||||||
font-size: 0.7rem;
|
|
||||||
border-radius: 0.5em;
|
|
||||||
margin-right: 0.1rem;
|
|
||||||
}
|
|
||||||
zone-content tbody tr .rdata dd {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
zone-content tbody tr .rdata dl {
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
zone-content tbody tr .rdata div {
|
|
||||||
margin: 0.1rem 0.5rem 0.1rem 0;
|
|
||||||
display: flex;
|
|
||||||
align-items: baseline;
|
|
||||||
}
|
|
||||||
|
|
|
@ -26,52 +26,52 @@ header > :not(:last-of-type) {
|
||||||
margin-right: 2ch;
|
margin-right: 2ch;
|
||||||
}
|
}
|
||||||
|
|
||||||
zone-content h3, zone-content h4 {
|
.zone-content h3, .zone-content h4 {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
width: 30%;
|
width: 30%;
|
||||||
}
|
}
|
||||||
|
|
||||||
zone-content article {
|
.zone-content article {
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
zone-content > article > div {
|
.zone-content > article > div {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
zone-content > article {
|
.zone-content > article {
|
||||||
margin: .5rem 0;
|
margin: .5rem 0;
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
zone-content > article:not(:last-of-type) {
|
.zone-content > article:not(:last-of-type) {
|
||||||
border-bottom: 2px solid var(--color-hightlight-2);
|
border-bottom: 2px solid rgb(var(--color-hightlight-2));
|
||||||
}
|
}
|
||||||
|
|
||||||
zone-content article > *{
|
.zone-content article > *{
|
||||||
margin-right: 2ch;
|
margin-right: 2ch;
|
||||||
}
|
}
|
||||||
|
|
||||||
zone-content article ul {
|
.zone-content article ul {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
list-style-type: none;
|
list-style-type: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
zone-content article dl {
|
.zone-content article dl {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template: auto / max-content 1fr;
|
grid-template: auto / max-content 1fr;
|
||||||
}
|
}
|
||||||
|
|
||||||
zone-content article dd {
|
.zone-content article dd {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
zone-content article dt span {
|
.zone-content article dt span {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
background-color: var(--color-hightlight-1);
|
background-color: rgb(var(--color-hightlight-1));
|
||||||
padding: 0.1em 0.5em;
|
padding: 0.1em 0.5em;
|
||||||
border-radius: 0.5em;
|
border-radius: 0.5em;
|
||||||
margin-right: 0.1rem;
|
margin-right: 0.1rem;
|
||||||
|
@ -79,34 +79,79 @@ zone-content article dt span {
|
||||||
}
|
}
|
||||||
|
|
||||||
form.new-record {
|
form.new-record {
|
||||||
display: flex;
|
width: auto;
|
||||||
flex-direction: row;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
form.new-record > div {
|
form.new-record > div.form-row > div:not(:last-child) {
|
||||||
display: flex;
|
flex-grow: 1;
|
||||||
flex-direction: column;
|
|
||||||
margin-right: 2ch;
|
margin-right: 2ch;
|
||||||
}
|
}
|
||||||
|
|
||||||
form.new-record > div:first-child {
|
form.new-record > div.form-row {
|
||||||
width: 30%;
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
form.new-record > div:first-child span {
|
form.new-record > div.form-row > div:first-child {
|
||||||
|
min-width: 30%;
|
||||||
|
}
|
||||||
|
|
||||||
|
form.new-record > div.form-row > div:nth-child(2) {
|
||||||
|
min-width: calc( .3 * (70% - 4ch));
|
||||||
|
}
|
||||||
|
|
||||||
|
form div.input-group {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
form div.combined-input {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
form div.combined-input input {
|
||||||
|
height: min-content;
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
form div.combined-input span {
|
||||||
font-size: .8rem;
|
font-size: .8rem;
|
||||||
|
padding: .35rem;
|
||||||
|
border: 1px solid rgb(var(--color-primary));;
|
||||||
|
border-left: none;
|
||||||
|
background: rgba(var(--color-hightlight-2),.2);
|
||||||
}
|
}
|
||||||
|
|
||||||
form.new-record > div:nth-child(2) {
|
form.disabled {
|
||||||
width: calc( .3 * (70% - 4ch));
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input[name^="ttl"] {
|
||||||
form.new-record > div:nth-child(2) select {
|
max-width: 10ch;
|
||||||
flex-grow: 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
form.new-record > div:nth-child(3) {
|
form.new-record button,
|
||||||
flex: 1;
|
form.new-record input[type="submit"] {
|
||||||
|
margin-right: 1ch;
|
||||||
|
margin-top: .75rem;
|
||||||
|
}
|
||||||
|
form.new-record h3 {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
form.new-record .preview {
|
||||||
|
margin: .5rem 0;
|
||||||
|
border: 1px solid rgb(var(--color-primary));
|
||||||
|
}
|
||||||
|
|
||||||
|
form.new-record .preview pre {
|
||||||
|
margin: .5rem 0 .5rem 0;
|
||||||
|
padding: 0 .5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
form.new-record .preview h4 {
|
||||||
|
margin: 0;
|
||||||
|
padding: .0rem .5rem 0 .5rem;;
|
||||||
|
border-bottom: 1px solid rgb(var(--color-primary));
|
||||||
|
font-size: 1rem;
|
||||||
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue