feat: add chats list (#17)
This commit is contained in:
parent
d74a763224
commit
67dd3dc260
|
@ -0,0 +1,108 @@
|
|||
// Copyright 2023 The casbin Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/casbin/casibase/object"
|
||||
)
|
||||
|
||||
func (c *ApiController) GetGlobalChats() {
|
||||
chats, err := object.GetGlobalChats()
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.ResponseOk(chats)
|
||||
}
|
||||
|
||||
func (c *ApiController) GetChats() {
|
||||
owner := c.Input().Get("owner")
|
||||
|
||||
chats, err := object.GetChats(owner)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.ResponseOk(chats)
|
||||
}
|
||||
|
||||
func (c *ApiController) GetChat() {
|
||||
id := c.Input().Get("id")
|
||||
|
||||
chat, err := object.GetChat(id)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.ResponseOk(chat)
|
||||
}
|
||||
|
||||
func (c *ApiController) UpdateChat() {
|
||||
id := c.Input().Get("id")
|
||||
|
||||
var chat object.Chat
|
||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &chat)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
success, err := object.UpdateChat(id, &chat)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.ResponseOk(success)
|
||||
}
|
||||
|
||||
func (c *ApiController) AddChat() {
|
||||
var chat object.Chat
|
||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &chat)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
success, err := object.AddChat(&chat)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.ResponseOk(success)
|
||||
}
|
||||
|
||||
func (c *ApiController) DeleteChat() {
|
||||
var chat object.Chat
|
||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &chat)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
success, err := object.DeleteChat(&chat)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.ResponseOk(success)
|
||||
}
|
|
@ -127,4 +127,9 @@ func (a *Adapter) createTable() {
|
|||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
err = a.engine.Sync2(new(Chat))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,118 @@
|
|||
// Copyright 2023 The casbin Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package object
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/casbin/casibase/util"
|
||||
"xorm.io/core"
|
||||
)
|
||||
|
||||
type Chat struct {
|
||||
Owner string `xorm:"varchar(100) notnull pk" json:"owner"`
|
||||
Name string `xorm:"varchar(100) notnull pk" json:"name"`
|
||||
CreatedTime string `xorm:"varchar(100)" json:"createdTime"`
|
||||
UpdatedTime string `xorm:"varchar(100)" json:"updatedTime"`
|
||||
|
||||
//Organization string `xorm:"varchar(100)" json:"organization"`
|
||||
DisplayName string `xorm:"varchar(100)" json:"displayName"`
|
||||
Category string `xorm:"varchar(100)" json:"category"`
|
||||
Type string `xorm:"varchar(100)" json:"type"`
|
||||
User1 string `xorm:"varchar(100)" json:"user1"`
|
||||
User2 string `xorm:"varchar(100)" json:"user2"`
|
||||
Users []string `xorm:"varchar(100)" json:"users"`
|
||||
MessageCount int `json:"messageCount"`
|
||||
}
|
||||
|
||||
func GetGlobalChats() ([]*Chat, error) {
|
||||
chats := []*Chat{}
|
||||
err := adapter.engine.Asc("owner").Desc("created_time").Find(&chats)
|
||||
if err != nil {
|
||||
return chats, err
|
||||
}
|
||||
|
||||
return chats, nil
|
||||
}
|
||||
|
||||
func GetChats(owner string) ([]*Chat, error) {
|
||||
chats := []*Chat{}
|
||||
err := adapter.engine.Desc("created_time").Find(&chats, &Chat{Owner: owner})
|
||||
if err != nil {
|
||||
return chats, err
|
||||
}
|
||||
|
||||
return chats, nil
|
||||
}
|
||||
|
||||
func getChat(owner, name string) (*Chat, error) {
|
||||
chat := Chat{Owner: owner, Name: name}
|
||||
existed, err := adapter.engine.Get(&chat)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if existed {
|
||||
return &chat, nil
|
||||
} else {
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
func GetChat(id string) (*Chat, error) {
|
||||
owner, name := util.GetOwnerAndNameFromId(id)
|
||||
return getChat(owner, name)
|
||||
}
|
||||
|
||||
func UpdateChat(id string, chat *Chat) (bool, error) {
|
||||
owner, name := util.GetOwnerAndNameFromId(id)
|
||||
_, err := getChat(owner, name)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if chat == nil {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
_, err = adapter.engine.ID(core.PK{owner, name}).AllCols().Update(chat)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
//return affected != 0
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func AddChat(chat *Chat) (bool, error) {
|
||||
affected, err := adapter.engine.Insert(chat)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return affected != 0, nil
|
||||
}
|
||||
|
||||
func DeleteChat(chat *Chat) (bool, error) {
|
||||
affected, err := adapter.engine.ID(core.PK{chat.Owner, chat.Name}).Delete(&Chat{})
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return affected != 0, nil
|
||||
}
|
||||
|
||||
func (chat *Chat) GetId() string {
|
||||
return fmt.Sprintf("%s/%s", chat.Owner, chat.Name)
|
||||
}
|
|
@ -74,6 +74,13 @@ func initAPI() {
|
|||
beego.Router("/api/add-provider", &controllers.ApiController{}, "POST:AddProvider")
|
||||
beego.Router("/api/delete-provider", &controllers.ApiController{}, "POST:DeleteProvider")
|
||||
|
||||
beego.Router("/api/get-global-chats", &controllers.ApiController{}, "GET:GetGlobalChats")
|
||||
beego.Router("/api/get-chats", &controllers.ApiController{}, "GET:GetChats")
|
||||
beego.Router("/api/get-chat", &controllers.ApiController{}, "GET:GetChat")
|
||||
beego.Router("/api/update-chat", &controllers.ApiController{}, "POST:UpdateChat")
|
||||
beego.Router("/api/add-chat", &controllers.ApiController{}, "POST:AddChat")
|
||||
beego.Router("/api/delete-chat", &controllers.ApiController{}, "POST:DeleteChat")
|
||||
|
||||
beego.Router("/api/update-file", &controllers.ApiController{}, "POST:UpdateFile")
|
||||
beego.Router("/api/add-file", &controllers.ApiController{}, "POST:AddFile")
|
||||
beego.Router("/api/delete-file", &controllers.ApiController{}, "POST:DeleteFile")
|
||||
|
|
|
@ -38,6 +38,8 @@ import ProviderEditPage from "./ProviderEditPage";
|
|||
import SigninPage from "./SigninPage";
|
||||
import i18next from "i18next";
|
||||
import LanguageSelect from "./LanguageSelect";
|
||||
import ChatEditPage from "./ChatEditPage";
|
||||
import ChatListPage from "./ChatListPage";
|
||||
|
||||
const {Header, Footer} = Layout;
|
||||
|
||||
|
@ -88,6 +90,8 @@ class App extends Component {
|
|||
this.setState({selectedMenuKey: "/videos"});
|
||||
} else if (uri.includes("/providers")) {
|
||||
this.setState({selectedMenuKey: "/providers"});
|
||||
} else if (uri.includes("/chats")) {
|
||||
this.setState({selectedMenuKey: "/chats"});
|
||||
} else {
|
||||
this.setState({selectedMenuKey: "null"});
|
||||
}
|
||||
|
@ -314,6 +318,13 @@ class App extends Component {
|
|||
</Link>
|
||||
</Menu.Item>
|
||||
);
|
||||
res.push(
|
||||
<Menu.Item key="/chats">
|
||||
<Link to="/chats">
|
||||
{i18next.t("general:Chats")}
|
||||
</Link>
|
||||
</Menu.Item>
|
||||
);
|
||||
|
||||
if (Setting.isLocalAdminUser(this.state.account)) {
|
||||
res.push(
|
||||
|
@ -390,6 +401,8 @@ class App extends Component {
|
|||
<Route exact path="/videos/:videoName" render={(props) => this.renderSigninIfNotSignedIn(<VideoEditPage account={this.state.account} {...props} />)} />
|
||||
<Route exact path="/providers" render={(props) => this.renderSigninIfNotSignedIn(<ProviderListPage account={this.state.account} {...props} />)} />
|
||||
<Route exact path="/providers/:providerName" render={(props) => this.renderSigninIfNotSignedIn(<ProviderEditPage account={this.state.account} {...props} />)} />
|
||||
<Route exact path="/chats" render={(props) => this.renderSigninIfNotSignedIn(<ChatListPage account={this.state.account} {...props} />)} />
|
||||
<Route exact path="/chats/:chatName" render={(props) => this.renderSigninIfNotSignedIn(<ChatEditPage account={this.state.account} {...props} />)} />
|
||||
</Switch>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -0,0 +1,218 @@
|
|||
// Copyright 2023 The casbin Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import React from "react";
|
||||
import {Button, Card, Col, Input, Row, Select} from "antd";
|
||||
import * as ChatBackend from "./backend/ChatBackend";
|
||||
import * as Setting from "./Setting";
|
||||
import i18next from "i18next";
|
||||
|
||||
const {Option} = Select;
|
||||
|
||||
class ChatEditPage extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
classes: props,
|
||||
chatName: props.match.params.chatName,
|
||||
chat: null,
|
||||
};
|
||||
}
|
||||
|
||||
UNSAFE_componentWillMount() {
|
||||
this.getChat();
|
||||
}
|
||||
|
||||
getChat() {
|
||||
ChatBackend.getChat(this.props.account.name, this.state.chatName)
|
||||
.then((chat) => {
|
||||
if (chat.status === "ok") {
|
||||
this.setState({
|
||||
chat: chat.data,
|
||||
});
|
||||
} else {
|
||||
Setting.showMessage("error", `Failed to get chat: ${chat.msg}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
parseChatField(key, value) {
|
||||
if ([""].includes(key)) {
|
||||
value = Setting.myParseInt(value);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
updateChatField(key, value) {
|
||||
value = this.parseChatField(key, value);
|
||||
|
||||
const chat = this.state.chat;
|
||||
chat[key] = value;
|
||||
this.setState({
|
||||
chat: chat,
|
||||
});
|
||||
}
|
||||
|
||||
renderChat() {
|
||||
return (
|
||||
<Card size="small" title={
|
||||
<div>
|
||||
{i18next.t("chat:Edit Chat")}
|
||||
<Button type="primary" onClick={this.submitChatEdit.bind(this)}>{i18next.t("general:Save")}</Button>
|
||||
</div>
|
||||
} style={(Setting.isMobile()) ? {margin: "5px"} : {}} type="inner">
|
||||
{/* <Row style={{marginTop: "10px"}} >*/}
|
||||
{/* <Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>*/}
|
||||
{/* {Setting.getLabel(i18next.t("general:Organization"), i18next.t("general:Organization - Tooltip"))} :*/}
|
||||
{/* </Col>*/}
|
||||
{/* <Col span={22} >*/}
|
||||
{/* <Select virtual={false} disabled={!Setting.isAdminUser(this.props.account)} style={{width: "100%"}} value={this.state.chat.organization} onChange={(value => {this.updateChatField("organization", value);})}*/}
|
||||
{/* options={this.state.organizations.map((organization) => Setting.getOption(organization.name, organization.name))*/}
|
||||
{/* } />*/}
|
||||
{/* </Col>*/}
|
||||
{/* </Row>*/}
|
||||
<Row style={{marginTop: "10px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{i18next.t("general:Name")} :
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Input value={this.state.chat.name} onChange={e => {
|
||||
this.updateChatField("name", e.target.value);
|
||||
}} />
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{i18next.t("general:Display name")} :
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Input value={this.state.chat.displayName} onChange={e => {
|
||||
this.updateChatField("displayName", e.target.value);
|
||||
}} />
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{i18next.t("chat:Type")} :
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Select virtual={false} style={{width: "100%"}} value={this.state.chat.type} onChange={(value => {
|
||||
this.updateChatField("type", value);
|
||||
})}>
|
||||
{
|
||||
[
|
||||
{id: "Single", name: i18next.t("chat:Single")},
|
||||
{id: "Group", name: i18next.t("chat:Group")},
|
||||
{id: "AI", name: i18next.t("chat:AI")},
|
||||
].map((item, index) => <Option key={index} value={item.id}>{item.name}</Option>)
|
||||
}
|
||||
</Select>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{i18next.t("chat:Category")} :
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Input value={this.state.chat.category} onChange={e => {
|
||||
this.updateChatField("category", e.target.value);
|
||||
}} />
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{i18next.t("chat:User1")} :
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Select virtual={false} style={{width: "100%"}} value={this.state.chat.user1} onChange={(value => {this.updateChatField("user1", value);})}
|
||||
options={this.state.chat.users.map((user) => Setting.getOption(`${user}`, `${user}`))
|
||||
} />
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{i18next.t("chat:User2")} :
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Select virtual={false} style={{width: "100%"}} value={this.state.chat.user2} onChange={(value => {this.updateChatField("user2", value);})}
|
||||
options={[{label: "None", value: ""}, ...this.state.chat.users.map((user) => Setting.getOption(`${user}`, `${user}`))]
|
||||
} />
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{i18next.t("chat:Users")} :
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Select virtual={false} mode="multiple" style={{width: "100%"}} value={this.state.chat.users}
|
||||
onChange={(value => {this.updateChatField("users", value);})}
|
||||
options={this.state.chat.users.map((user) => Setting.getOption(`${user}`, `${user}`))}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
submitChatEdit() {
|
||||
const chat = Setting.deepCopy(this.state.chat);
|
||||
ChatBackend.updateChat(this.state.chat.owner, this.state.chatName, chat)
|
||||
.then((res) => {
|
||||
if (res.status === "ok") {
|
||||
if (res.data) {
|
||||
Setting.showMessage("success", "Successfully saved");
|
||||
this.setState({
|
||||
chatName: this.state.chat.name,
|
||||
});
|
||||
this.props.history.push(`/chats/${this.state.chat.name}`);
|
||||
} else {
|
||||
Setting.showMessage("error", "failed to save: server side failure");
|
||||
this.updateChatField("name", this.state.chatName);
|
||||
}
|
||||
} else {
|
||||
Setting.showMessage("error", `failed to save: ${res.msg}`);
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
Setting.showMessage("error", `failed to save: ${error}`);
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<Row style={{width: "100%"}}>
|
||||
<Col span={1}>
|
||||
</Col>
|
||||
<Col span={22}>
|
||||
{
|
||||
this.state.chat !== null ? this.renderChat() : null
|
||||
}
|
||||
</Col>
|
||||
<Col span={1}>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{margin: 10}}>
|
||||
<Col span={2}>
|
||||
</Col>
|
||||
<Col span={18}>
|
||||
<Button type="primary" onClick={this.submitChatEdit.bind(this)}>{i18next.t("general:Save")}</Button>
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default ChatEditPage;
|
|
@ -0,0 +1,275 @@
|
|||
// Copyright 2023 The casbin Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import React from "react";
|
||||
import {Link} from "react-router-dom";
|
||||
import {Button, Col, Popconfirm, Row, Table} from "antd";
|
||||
import * as Setting from "./Setting";
|
||||
import * as ChatBackend from "./backend/ChatBackend";
|
||||
import moment from "moment";
|
||||
import i18next from "i18next";
|
||||
|
||||
class ChatListPage extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
classes: props,
|
||||
chats: null,
|
||||
};
|
||||
}
|
||||
|
||||
UNSAFE_componentWillMount() {
|
||||
this.getChats();
|
||||
}
|
||||
|
||||
getChats() {
|
||||
ChatBackend.getChats(this.props.account.name)
|
||||
.then((res) => {
|
||||
if (res.status === "ok") {
|
||||
this.setState({
|
||||
chats: res.data,
|
||||
});
|
||||
} else {
|
||||
Setting.showMessage("error", `Failed to get chats: ${res.msg}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
newChat() {
|
||||
const randomName = Setting.getRandomName();
|
||||
return {
|
||||
owner: this.props.account.name,
|
||||
name: `chat_${randomName}`,
|
||||
createdTime: moment().format(),
|
||||
updatedTime: moment().format(),
|
||||
displayName: `New Chat - ${randomName}`,
|
||||
category: "Chat Category - 1",
|
||||
type: "Single",
|
||||
user1: `${this.props.account.owner}/${this.props.account.name}`,
|
||||
user2: "",
|
||||
users: [`${this.props.account.owner}/${this.props.account.name}`],
|
||||
messageCount: 0,
|
||||
};
|
||||
}
|
||||
|
||||
addChat() {
|
||||
const newChat = this.newChat();
|
||||
ChatBackend.addChat(newChat)
|
||||
.then((res) => {
|
||||
if (res.status === "ok") {
|
||||
Setting.showMessage("success", "Chat added successfully");
|
||||
this.setState({
|
||||
chats: Setting.prependRow(this.state.chats, newChat),
|
||||
});
|
||||
} else {
|
||||
Setting.showMessage("error", `Failed to add Chat: ${res.msg}`);
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
Setting.showMessage("error", `Chat failed to add: ${error}`);
|
||||
});
|
||||
}
|
||||
|
||||
deleteChat(i) {
|
||||
ChatBackend.deleteChat(this.state.chats[i])
|
||||
.then((res) => {
|
||||
if (res.status === "ok") {
|
||||
Setting.showMessage("success", "Chat deleted successfully");
|
||||
this.setState({
|
||||
chats: Setting.deleteRow(this.state.chats, i),
|
||||
});
|
||||
} else {
|
||||
Setting.showMessage("error", `Failed to delete Chat: ${res.msg}`);
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
Setting.showMessage("error", `Chat failed to delete: ${error}`);
|
||||
});
|
||||
}
|
||||
|
||||
renderTable(chats) {
|
||||
const columns = [
|
||||
{
|
||||
title: i18next.t("general:Name"),
|
||||
dataIndex: "name",
|
||||
key: "name",
|
||||
width: "140px",
|
||||
sorter: (a, b) => a.name.localeCompare(b.name),
|
||||
render: (text, record, index) => {
|
||||
return (
|
||||
<Link to={`chats/${text}`}>
|
||||
{text}
|
||||
</Link>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: i18next.t("general:Created time"),
|
||||
dataIndex: "createdTime",
|
||||
key: "createdTime",
|
||||
width: "150px",
|
||||
sorter: true,
|
||||
render: (text, record, index) => {
|
||||
return Setting.getFormattedDate(text);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: i18next.t("general:Updated time"),
|
||||
dataIndex: "updatedTime",
|
||||
key: "updatedTime",
|
||||
width: "15 0px",
|
||||
sorter: true,
|
||||
render: (text, record, index) => {
|
||||
return Setting.getFormattedDate(text);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: i18next.t("general:Display name"),
|
||||
dataIndex: "displayName",
|
||||
key: "displayName",
|
||||
// width: '100px',
|
||||
sorter: true,
|
||||
// ...this.getColumnSearchProps("displayName"),
|
||||
},
|
||||
{
|
||||
title: i18next.t("chat:Type"),
|
||||
dataIndex: "type",
|
||||
key: "type",
|
||||
width: "110px",
|
||||
sorter: true,
|
||||
filterMultiple: false,
|
||||
filters: [
|
||||
{text: "Single", value: "Single"},
|
||||
{text: "Group", value: "Group"},
|
||||
{text: "AI", value: "AI"},
|
||||
],
|
||||
render: (text, record, index) => {
|
||||
return i18next.t(`chat:${text}`);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: i18next.t("chat:Category"),
|
||||
dataIndex: "category",
|
||||
key: "category",
|
||||
// width: '100px',
|
||||
sorter: true,
|
||||
// ...this.getColumnSearchProps("category"),
|
||||
},
|
||||
{
|
||||
title: i18next.t("chat:User1"),
|
||||
dataIndex: "user1",
|
||||
key: "user1",
|
||||
width: "120px",
|
||||
sorter: true,
|
||||
// ...this.getColumnSearchProps("user1"),
|
||||
render: (text, record, index) => {
|
||||
return (
|
||||
<Link to={`/users/${text}`}>
|
||||
{text}
|
||||
</Link>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: i18next.t("chat:User2"),
|
||||
dataIndex: "user2",
|
||||
key: "user2",
|
||||
width: "120px",
|
||||
sorter: true,
|
||||
// ...this.getColumnSearchProps("user2"),
|
||||
render: (text, record, index) => {
|
||||
return (
|
||||
<Link to={`/users/${text}`}>
|
||||
{text}
|
||||
</Link>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: i18next.t("general:Users"),
|
||||
dataIndex: "users",
|
||||
key: "users",
|
||||
// width: '100px',
|
||||
sorter: true,
|
||||
// ...this.getColumnSearchProps("users"),
|
||||
render: (text, record, index) => {
|
||||
return Setting.getTags(text, "users");
|
||||
},
|
||||
},
|
||||
{
|
||||
title: i18next.t("chat:Message count"),
|
||||
dataIndex: "messageCount",
|
||||
key: "messageCount",
|
||||
// width: '100px',
|
||||
sorter: true,
|
||||
// ...this.getColumnSearchProps("messageCount"),
|
||||
},
|
||||
{
|
||||
title: i18next.t("general:Action"),
|
||||
dataIndex: "action",
|
||||
key: "action",
|
||||
width: "180px",
|
||||
render: (text, record, index) => {
|
||||
return (
|
||||
<div>
|
||||
<Button style={{marginTop: "10px", marginBottom: "10px", marginRight: "10px"}} type="primary" onClick={() => this.props.history.push(`/chats/${record.name}`)}>{i18next.t("general:Edit")}</Button>
|
||||
<Popconfirm
|
||||
title={`Sure to delete chat: ${record.name} ?`}
|
||||
onConfirm={() => this.deleteChat(index)}
|
||||
okText="OK"
|
||||
cancelText="Cancel"
|
||||
>
|
||||
<Button style={{marginBottom: "10px"}} type="danger">{i18next.t("general:Delete")}</Button>
|
||||
</Popconfirm>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Table columns={columns} dataSource={chats} rowKey="name" size="middle" bordered pagination={{pageSize: 100}}
|
||||
title={() => (
|
||||
<div>
|
||||
{i18next.t("chat:Chats")}
|
||||
<Button type="primary" size="small" onClick={this.addChat.bind(this)}>{i18next.t("general:Add")}</Button>
|
||||
</div>
|
||||
)}
|
||||
loading={chats === null}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<Row style={{width: "100%"}}>
|
||||
<Col span={1}>
|
||||
</Col>
|
||||
<Col span={22}>
|
||||
{
|
||||
this.renderTable(this.state.chats)
|
||||
}
|
||||
</Col>
|
||||
<Col span={1}>
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default ChatListPage;
|
|
@ -289,11 +289,19 @@ export function getTag(text, type, state) {
|
|||
}
|
||||
}
|
||||
|
||||
export function getTags(vectors) {
|
||||
export function getTags(vectors, type) {
|
||||
if (!vectors) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (type === "vectors") {
|
||||
return getVectorTag(vectors);
|
||||
} else if (type === "users") {
|
||||
return getUserTag(vectors);
|
||||
}
|
||||
}
|
||||
|
||||
function getVectorTag(vectors) {
|
||||
const res = [];
|
||||
vectors.forEach((vector, i) => {
|
||||
if (vector.data.length !== 0) {
|
||||
|
@ -315,6 +323,28 @@ export function getTags(vectors) {
|
|||
return res;
|
||||
}
|
||||
|
||||
function getUserTag(users) {
|
||||
const res = [];
|
||||
users.forEach((user, i) => {
|
||||
if (user.length !== 0) {
|
||||
res.push(
|
||||
<Tooltip placement="top" title={getShortText(JSON.stringify(user), 500)}>
|
||||
<Tag color={"success"}>
|
||||
{user}
|
||||
</Tag>
|
||||
</Tooltip>
|
||||
);
|
||||
} else {
|
||||
res.push(
|
||||
<Tag color={"warning"}>
|
||||
{user}
|
||||
</Tag>
|
||||
);
|
||||
}
|
||||
});
|
||||
return res;
|
||||
}
|
||||
|
||||
export function getLabelTags(labels) {
|
||||
if (!labels) {
|
||||
return [];
|
||||
|
@ -593,3 +623,10 @@ export function getItem(label, key, icon, children, type) {
|
|||
type,
|
||||
};
|
||||
}
|
||||
|
||||
export function getOption(label, value) {
|
||||
return {
|
||||
label,
|
||||
value,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -164,7 +164,7 @@ class VectorsetListPage extends React.Component {
|
|||
// width: '120px',
|
||||
sorter: (a, b) => a.vectors.localeCompare(b.vectors),
|
||||
render: (text, record, index) => {
|
||||
return Setting.getTags(text);
|
||||
return Setting.getTags(text, "vectors");
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
|
@ -124,7 +124,7 @@ class WordsetListPage extends React.Component {
|
|||
// width: '120px',
|
||||
sorter: (a, b) => a.vectors.localeCompare(b.vectors),
|
||||
render: (text, record, index) => {
|
||||
return Setting.getTags(text);
|
||||
return Setting.getTags(text, "vectors");
|
||||
},
|
||||
},
|
||||
// {
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
// Copyright 2023 The casbin Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import * as Setting from "../Setting";
|
||||
|
||||
export function getGlobalChats() {
|
||||
return fetch(`${Setting.ServerUrl}/api/get-global-chats`, {
|
||||
method: "GET",
|
||||
credentials: "include",
|
||||
}).then(res => res.json());
|
||||
}
|
||||
|
||||
export function getChats(owner) {
|
||||
return fetch(`${Setting.ServerUrl}/api/get-chats?owner=${owner}`, {
|
||||
method: "GET",
|
||||
credentials: "include",
|
||||
}).then(res => res.json());
|
||||
}
|
||||
|
||||
export function getChat(owner, name) {
|
||||
return fetch(`${Setting.ServerUrl}/api/get-chat?id=${owner}/${encodeURIComponent(name)}`, {
|
||||
method: "GET",
|
||||
credentials: "include",
|
||||
}).then(res => res.json());
|
||||
}
|
||||
|
||||
export function updateChat(owner, name, chat) {
|
||||
const newChat = Setting.deepCopy(chat);
|
||||
return fetch(`${Setting.ServerUrl}/api/update-chat?id=${owner}/${encodeURIComponent(name)}`, {
|
||||
method: "POST",
|
||||
credentials: "include",
|
||||
body: JSON.stringify(newChat),
|
||||
}).then(res => res.json());
|
||||
}
|
||||
|
||||
export function addChat(chat) {
|
||||
const newChat = Setting.deepCopy(chat);
|
||||
return fetch(`${Setting.ServerUrl}/api/add-chat`, {
|
||||
method: "POST",
|
||||
credentials: "include",
|
||||
body: JSON.stringify(newChat),
|
||||
}).then(res => res.json());
|
||||
}
|
||||
|
||||
export function deleteChat(chat) {
|
||||
const newChat = Setting.deepCopy(chat);
|
||||
return fetch(`${Setting.ServerUrl}/api/delete-chat`, {
|
||||
method: "POST",
|
||||
credentials: "include",
|
||||
body: JSON.stringify(newChat),
|
||||
}).then(res => res.json());
|
||||
}
|
Loading…
Reference in New Issue