--- /dev/null
+/* sidebar_editor.js */
+var feedsEditor = {};
+var vm;
+
+Vue.component('feed-editor', {
+ template: '#feed-editor',
+ props: {
+ feed: Object,
+ },
+ data: function () {
+ return { message: "" };
+ },
+ methods: {
+ save: function save() {
+ // create/update feed
+ this.message = "";
+ var postData = {
+ action: this.feed.id == 0 ? "create" : "update",
+ url: this.feed.url,
+ title: this.feed.title,
+ description: this.feed.description,
+ };
+ this.$http.post("/api/v1/admin/feed", postData).then(
+ (response) => { // success
+ if (response.body.error) {
+ this.message = "error: " + response.body.message;
+ return;
+ }
+ if (!response.body.id) {
+ this.message = "error: " + response.body.message;
+ return;
+ }
+ this.message = "saved.";
+ if (this.feed.id == 0) {
+ this.feed.id = response.body.id;
+ }
+ },
+ (response) => { // fail
+ }
+ );
+ },
+ close: function close() {
+ this.feed.editing = false;
+ this.message = "";
+ if (this.feed.id == 0) {
+ vm.feeds.pop();
+ }
+ },
+ fetch: function fetch() {
+ this.message = "";
+ var postData = {
+ action: "fetch",
+ url: this.feed.url,
+ };
+ this.$http.post("/api/v1/admin/feed", postData).then(
+ (response) => { // success
+ if (response.body.error) {
+ this.message = "error: " + response.body.message;
+ return;
+ }
+ this.feed.url = response.body.item.url;
+ this.feed.title = response.body.item.title;
+ this.feed.description = response.body.item.description;
+ },
+ (response) => { // fail
+ this.message = "error: " + response.body.message;
+ return;
+ }
+ );
+
+ },
+ },
+});
+
+
+feedsEditor.run = function run (params) {
+ var data = {
+ message: '',
+ feeds: [],
+ };
+ var computed = {};
+ var methods = {
+ editItem: function editItem(item) {
+ Vue.set(item, 'editing', true);
+ },
+ deleteItem: function deleteItem() {
+ var target = [];
+ for (var i = 0; i < this.feeds.length; i++) {
+ if (this.feeds[i].selected) {
+ target.push({id: this.feeds[i].id, index: i});
+ }
+ }
+ if (target.length == 0) {
+ return;
+ }
+ var msg = "delete " + target.length + " item(s)?";
+ var result = window.confirm(msg);
+ if (result) {
+ // delete items
+ target.reverse();
+ target.forEach(item => {
+ this.$http.post("/api/v1/admin/feed", {id: item.id}).then(
+ (response) => { // success
+ if (response.body.error) {
+ this.message = "error: " + response.body.message;
+ return;
+ }
+ this.feeds.splice(item.index, 1);
+ },
+ (response) => { // fail
+ }
+ );
+ });
+ }
+ },
+ newItem: function newItem() {
+ var item = {
+ id: 0,
+ title: '',
+ description: '',
+ url: '',
+ selected: false,
+ editing: true,
+ };
+ var it = this.feeds.push(item);
+ },
+ };
+
+ function initItems (items) {
+ items.forEach(item => {
+ Vue.set(item, 'selected', false);
+ Vue.set(item, 'editing', false);
+ });
+ };
+
+ vm = new Vue({el: params.el,
+ data: data,
+ computed: computed,
+ methods: methods,
+ created: function () {
+ this.$http.get("/api/v1/admin/feed").then(
+ (resp) => { // success
+ this.feeds = resp.body.items || [];
+ this.message = resp.body.message || '';
+ initItems(this.feeds);
+ },
+ (resp) => { // fail
+ this.feeds = [];
+ this.message = resp.body.message || "failed to get items";
+ }
+ );
+ },
+ });
+};
--- /dev/null
+[% WRAPPER common/layout %]
+
+<div class="app-frame" id="feeds-manager">
+ <h3>Feeds</h3>
+ <div v-text="message">
+ </div>
+ <form class="items">
+ <table class="table table-hover">
+ <thead>
+ <tr>
+ <th></th>
+ <th>ID</th>
+ <th>title</th>
+ <th>description</th>
+ <th>url</th>
+ <th>status</th>
+ <th>last fetched</th>
+ <th>last status</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr v-for="item in feeds" v-on:dblclick="editItem(item)">
+ <td><input type="checkbox" v-model="item.selected" /></td>
+ <td v-text="item.feed_id"></td>
+ <td v-text="item.title"></td>
+ <td v-text="item.description"></td>
+ <td v-text="item.url"></td>
+ <td v-text="item.status"></td>
+ <td v-text="item.latest_fetch_time"></td>
+ <td v-text="item.latest_fetch_result"></td>
+ <td><button class="btn btn-default" type="button" v-on:click="editItem(item)">edit</button></td>
+ </tr>
+ </tbody>
+ </table>
+
+ <div v-for="item in feeds">
+ <feed-editor :feed="item" v-if="item.editing"></feed-editor>
+ </div>
+
+ <div class="actions">
+ <button class="btn btn-default" type="button" v-on:click="newItem()">New</button>
+ <button class="btn btn-default" type="button" v-on:click="deleteItem()">Delete</button>
+ </div>
+ </form>
+</div>
+
+<script type="text/x-template" id="feed-editor">
+ <div class="modal vue-modal" v-if="feed.editing">
+ <div class="modal-dialog" role="document">
+ <div class="modal-content">
+ <form>
+ <div class="modal-header">
+ <span>feed edit</span>
+ </div>
+ <div class="modal-body">
+ <p class="form-inline">
+ <label for="feed-title">title: </label>
+ <input id="feed-title" type="text" class="form-control" name="title" v-model="feed.title" placeholder="title" />
+ </p>
+ <p class="form-group">
+ <label for="feed-description">description: </label>
+ <textarea id="feed-description" name="description" class="form-control" v-model="feed.description" placeholder="description" />
+ </p>
+ <p class="form-inline">
+ <label for="feed-url">url: </label>
+ <input id="feed-url" type="text" class="form-control" name="url" v-model="feed.url" placeholder="url" />
+ </p>
+ </div>
+ <div class="modal-footer">
+ <span v-text="message"></span>
+ <button type="submit" class="btn btn-default" v-on:click.prevent="fetch()">fetch title and description</button>
+ <button type="submit" class="btn btn-default" v-on:click.prevent="save()">save</button>
+ <button type="button" class="btn" v-on:click="close()">close</button>
+ </div>
+ </form>
+ </div>
+ </div>
+ </div>
+</script>
+
+<script src="/js/feeds-editor.js" ></script>
+<script>
+ feedsEditor.run({ el: '#feeds-manager' });
+</script>
+
+[% END %]