parent
542f275a26
commit
43829f4ef1
11 changed files with 194038 additions and 90 deletions
@ -1,28 +0,0 @@ |
||||
.App { |
||||
text-align: center; |
||||
} |
||||
|
||||
.App-logo { |
||||
animation: App-logo-spin infinite 20s linear; |
||||
height: 80px; |
||||
} |
||||
|
||||
.App-header { |
||||
background-color: #222; |
||||
height: 150px; |
||||
padding: 20px; |
||||
color: white; |
||||
} |
||||
|
||||
.App-title { |
||||
font-size: 1.5em; |
||||
} |
||||
|
||||
.App-intro { |
||||
font-size: large; |
||||
} |
||||
|
||||
@keyframes App-logo-spin { |
||||
from { transform: rotate(0deg); } |
||||
to { transform: rotate(360deg); } |
||||
} |
@ -1,21 +0,0 @@ |
||||
import React, { Component } from 'react'; |
||||
import logo from './logo.svg'; |
||||
import './App.css'; |
||||
|
||||
class App extends Component { |
||||
render() { |
||||
return ( |
||||
<div className="App"> |
||||
<header className="App-header"> |
||||
<img src={logo} className="App-logo" alt="logo" /> |
||||
<h1 className="App-title">Welcome to React</h1> |
||||
</header> |
||||
<p className="App-intro"> |
||||
To get started, edit <code>src/App.js</code> and save to reload. |
||||
</p> |
||||
</div> |
||||
); |
||||
} |
||||
} |
||||
|
||||
export default App; |
@ -0,0 +1,30 @@ |
||||
.ResultsTable { |
||||
text-align: center; |
||||
font-size: 0.8rem; |
||||
} |
||||
|
||||
.ResultsTable .bad { |
||||
color: #ae4b53; |
||||
background-color: #d7b0ae; |
||||
font-size: 1rem; |
||||
} |
||||
.ResultsTable .mediocre { |
||||
color: #c49863; |
||||
background-color: #e3d9ba; |
||||
font-size: 1rem; |
||||
} |
||||
.ResultsTable .good { |
||||
color: #46962b; |
||||
background-color: #bcd9b3; |
||||
font-size: 1rem; |
||||
} |
||||
|
||||
.ResultsTable td.text { |
||||
font-size: 0.8rem; |
||||
} |
||||
|
||||
.ResultsTable a.screenshot { |
||||
display: inline-block; |
||||
margin-left: 3px; |
||||
margin-right: 3px; |
||||
} |
@ -0,0 +1,255 @@ |
||||
import React, { Component } from 'react'; |
||||
import punycode from 'punycode'; |
||||
import './ResultsTable.css'; |
||||
import results from './spider_result.json'; |
||||
import screenshots from './screenshots.json'; |
||||
|
||||
class IconGood extends Component { |
||||
render() { |
||||
return <i className='icon ion-md-checkmark-circle' title={this.props.title}></i>; |
||||
} |
||||
} |
||||
|
||||
class IconBad extends Component { |
||||
render() { |
||||
return <i className='icon ion-md-close-circle' title={this.props.title}></i>; |
||||
} |
||||
} |
||||
|
||||
class CriteriumField extends Component { |
||||
render() { |
||||
if (this.props.type === 'positive') { |
||||
return <td key={this.props.keyProp} className='good'><IconGood title={this.props.title}/></td>; |
||||
} else { |
||||
return <td key={this.props.keyProp} className='bad'><IconBad title={this.props.title}/></td>; |
||||
} |
||||
} |
||||
} |
||||
|
||||
class CanonicalURLField extends Component { |
||||
render() { |
||||
if (this.props.data.value) { |
||||
return <CriteriumField keyProp='canonicalurl' type='positive' title='Verschiedene URL-Varianten werden auf eine einzige umgeleitet' /> |
||||
} |
||||
return <CriteriumField keyProp='canonicalurl' type='negative' title='Verschiedene URL-Varianten werden nicht auf eine einzige umgeleitet' /> |
||||
} |
||||
} |
||||
|
||||
class CityField extends Component { |
||||
render() { |
||||
return <td key='city'>{ this.props.city }</td>; |
||||
} |
||||
} |
||||
|
||||
class CMSField extends Component { |
||||
render() { |
||||
return <td key='cms'>{ this.props.cms }</td>; |
||||
} |
||||
} |
||||
|
||||
class DistrictField extends Component { |
||||
render() { |
||||
return <td key='district'>{ this.props.district }</td>; |
||||
} |
||||
} |
||||
|
||||
class FaviconField extends Component { |
||||
render() { |
||||
if (this.props.data.value) { |
||||
return <td className='good'>Ja</td>; |
||||
} |
||||
return <CriteriumField keyProp='favicon' type='negative' title='Die Site hat kein Icon' />; |
||||
} |
||||
} |
||||
|
||||
class FeedField extends Component { |
||||
render() { |
||||
if (this.props.data.value) { |
||||
return <CriteriumField keyProp='feed' type='positive' title='Die Site verweist auf mind. einen RSS-/Atom-Feed' /> |
||||
} |
||||
return <CriteriumField keyProp='feed' type='negative' title='Kein Link rel=alternate auf einen RSS-/Atom-Feed gefunden' /> |
||||
} |
||||
} |
||||
|
||||
class HTTPSField extends Component { |
||||
render() { |
||||
if (this.props.data.value) { |
||||
return <CriteriumField keyProp='https' type='positive' title='Die Site ist über HTTPS erreichbar' /> |
||||
} |
||||
return <CriteriumField keyProp='https' type='negative' title='Die Site ist nicht über HTTPS erreichbar (-2 Punkte)' /> |
||||
} |
||||
} |
||||
|
||||
class IPField extends Component { |
||||
render() { |
||||
if (this.props.ipaddresses && this.props.ipaddresses.length) { |
||||
return <td key='ipaddresses' className='good'>{ this.props.ipaddresses }</td>; |
||||
} |
||||
return <td key='ipaddresses' className='bad'><IconBad title='Der Domainname lässt sich nicht in eine IP-Adresse auflösen' /></td>; |
||||
} |
||||
} |
||||
|
||||
class ResponseDurationField extends Component { |
||||
render() { |
||||
var className = 'bad'; |
||||
if (this.props.data.score > 0) { |
||||
className = 'mediocre'; |
||||
} |
||||
if (this.props.data.score > 0.5) { |
||||
className = 'good'; |
||||
} |
||||
|
||||
if (this.props.data.value) { |
||||
return <td key='duration' className={className}>{ this.props.data.value } ms</td>; |
||||
} |
||||
return <CriteriumField keyProp='duration' type='negative' title='Keine Angabe' /> |
||||
} |
||||
} |
||||
|
||||
class ReachableField extends Component { |
||||
render() { |
||||
if (this.props.data.value) { |
||||
return <CriteriumField keyProp='reachable' type='positive' title='Die Site war beim Test erreichbar' /> |
||||
} |
||||
return <CriteriumField keyProp='reachable' type='negative' title='Die Site war beim letzten Test nicht erreichbar' /> |
||||
} |
||||
} |
||||
|
||||
class ResponsiveField extends Component { |
||||
render() { |
||||
if (this.props.data.value) { |
||||
return <CriteriumField keyProp='responsive' type='positive' title='Die Site ist offenbar auf mobilen Endgeräten nutzbar' /> |
||||
} |
||||
return <CriteriumField keyProp='responsive' type='negative' title='Die Site scheint mobile Endgeräte nicht zu unterstützen' /> |
||||
} |
||||
} |
||||
|
||||
class ScoreField extends Component { |
||||
render() { |
||||
return <td key='score'>{ this.props.score }</td>; |
||||
} |
||||
} |
||||
|
||||
class ScreenshotsField extends Component { |
||||
render() { |
||||
var screenshotElements = []; |
||||
|
||||
if (this.props.canonicalURLs && this.props.canonicalURLs.length > 0 && typeof screenshots[this.props.canonicalURLs[0]] !== 'undefined') { |
||||
var mobileScreenshot = 'http://green-spider-screenshots.sendung.de/320x640/'+screenshots[this.props.canonicalURLs[0]]; |
||||
var desktopScreenshot = 'http://green-spider-screenshots.sendung.de/1500x1500/'+screenshots[this.props.canonicalURLs[0]]; |
||||
screenshotElements.push(<a key='mobile' className='screenshot tt' href={mobileScreenshot} target='_blank' title='Screenshot für Smartphone-Ansicht anzeigen'><i className='icon ion-md-phone-portrait'></i></a>); |
||||
screenshotElements.push(<a key='desktop' className="screenshot tt" href={desktopScreenshot} target='_blank' title='Screenshot für Desktop-Ansicht anzeigen'><i className='icon ion-md-desktop'></i></a>); |
||||
} |
||||
|
||||
return <td key='screenshot'>{ screenshotElements }</td>; |
||||
} |
||||
} |
||||
|
||||
class StateField extends Component { |
||||
render() { |
||||
return <td key='state'>{ this.props.state }</td>; |
||||
} |
||||
} |
||||
|
||||
class TypeField extends Component { |
||||
render() { |
||||
var label = this.props.level; |
||||
if (label === 'DE:ORTSVERBAND') { |
||||
label = 'OV'; |
||||
} else if (label === 'DE:KREISVERBAND') { |
||||
label = 'KV'; |
||||
} else if (label === 'DE:REGIONALVERBAND') { |
||||
label = 'RV'; |
||||
} else if (label === 'DE:BEZIRKSVERBAND') { |
||||
label = 'BV'; |
||||
} else if (label === 'DE:LANDESVERBAND') { |
||||
label = 'LV'; |
||||
} |
||||
return <td key='type'>{ label }</td>; |
||||
} |
||||
} |
||||
|
||||
class URLField extends Component { |
||||
|
||||
trunc(s, length) { |
||||
if (s.length > length) { |
||||
s = s.substring(0, length) + '…'; |
||||
} |
||||
return s; |
||||
} |
||||
|
||||
render() { |
||||
var displayURL = this.trunc(punycode.toUnicode(this.props.url), 45); |
||||
return <td><a href={this.props.url} target="_blank" rel='noopener noreferrer'> { displayURL }</a></td>; |
||||
} |
||||
} |
||||
|
||||
class WWWOptionalField extends Component { |
||||
render() { |
||||
if (this.props.data.value) { |
||||
return <CriteriumField keyProp='wwwoptional' type='positive' title='Die Site ist sowohl mit als auch ohne www. in der URL aufrufbar' /> |
||||
} |
||||
return <CriteriumField keyProp='wwwoptional' type='negative' title='Die Site ist nicht sowohl mit als auch ohne www. in der URL aufrufbar' /> |
||||
} |
||||
} |
||||
|
||||
class ResultsTable extends Component { |
||||
render() { |
||||
|
||||
var rows = []; |
||||
results.forEach(element => { |
||||
console.log(element); |
||||
|
||||
var fields = [ |
||||
<TypeField level={element.meta.level} />, |
||||
<StateField state={element.meta.state} />, |
||||
<DistrictField district={element.meta.district} />, |
||||
<CityField city={element.meta.city} />, |
||||
<URLField url={element.input_url} />, |
||||
<ScoreField score={element.score} />, |
||||
<IPField ipaddresses={element.details.ipv4_addresses} />, |
||||
<ReachableField data={element.result.SITE_REACHABLE} />, |
||||
<ResponseDurationField data={element.result.HTTP_RESPONSE_DURATION} />, |
||||
<FaviconField data={element.result.FAVICON} />, |
||||
<HTTPSField data={element.result.HTTPS} />, |
||||
<WWWOptionalField data={element.result.WWW_OPTIONAL} />, |
||||
<CanonicalURLField data={element.result.CANONICAL_URL} />, |
||||
<ResponsiveField data={element.result.RESPONSIVE} />, |
||||
<FeedField data={element.result.FEEDS} />, |
||||
<ScreenshotsField canonicalURLs={element.details.canonical_urls} />, |
||||
<CMSField cms={element.details.cms} />, |
||||
]; |
||||
|
||||
rows.push(<tr key={element.input_url}>{ fields }</tr>) |
||||
}); |
||||
|
||||
return ( |
||||
<table className='table ResultsTable'> |
||||
<thead> |
||||
<tr> |
||||
<th scope='col'>Typ</th> |
||||
<th scope='col'>Land</th> |
||||
<th scope='col'>Kreis</th> |
||||
<th scope='col'>Stadt</th> |
||||
<th scope='col'>URL</th> |
||||
<th scope='col'>Score</th> |
||||
<th scope='col'>IP-Adresse</th> |
||||
<th scope='col'>Erreichbar</th> |
||||
<th scope='col'>Antwortzeit</th> |
||||
<th scope='col'>Icon</th> |
||||
<th scope='col'>HTTPS</th> |
||||
<th scope='col'>www. optional</th> |
||||
<th scope='col'>Kanonische URL</th> |
||||
<th scope='col'>Responsive</th> |
||||
<th scope='col'>Feed</th> |
||||
<th scope='col'>Screenshots</th> |
||||
<th scope='col'>CMS</th> |
||||
</tr> |
||||
</thead> |
||||
<tbody>{ rows }</tbody> |
||||
</table> |
||||
); |
||||
} |
||||
} |
||||
|
||||
export default ResultsTable; |
@ -1,9 +1,9 @@ |
||||
import React from 'react'; |
||||
import ReactDOM from 'react-dom'; |
||||
import App from './App'; |
||||
import ResultsTable from './ResultsTable'; |
||||
|
||||
it('renders without crashing', () => { |
||||
const div = document.createElement('div'); |
||||
ReactDOM.render(<App />, div); |
||||
ReactDOM.render(<ResultsTable />, div); |
||||
ReactDOM.unmountComponentAtNode(div); |
||||
}); |
@ -1,8 +1,9 @@ |
||||
import React from 'react'; |
||||
import ReactDOM from 'react-dom'; |
||||
import '../node_modules/bootstrap/dist/css/bootstrap.css'; |
||||
import './index.css'; |
||||
import App from './App'; |
||||
import ResultsTable from './ResultsTable'; |
||||
import registerServiceWorker from './registerServiceWorker'; |
||||
|
||||
ReactDOM.render(<App />, document.getElementById('root')); |
||||
ReactDOM.render(<ResultsTable />, document.getElementById('root')); |
||||
registerServiceWorker(); |
||||
|
Before Width: | Height: | Size: 2.6 KiB |
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Loading…
Reference in new issue