25/3/2021
This commit is contained in:
parent
74b4a383b4
commit
40fb126d37
@ -1,17 +0,0 @@
|
||||
{
|
||||
"presets": ["next/babel"],
|
||||
"plugins": [
|
||||
[
|
||||
"@babel/plugin-proposal-decorators",
|
||||
{
|
||||
"legacy": true
|
||||
}
|
||||
],
|
||||
[
|
||||
"@babel/plugin-proposal-class-properties",
|
||||
{
|
||||
"loose": false
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
@ -12,6 +12,8 @@
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"axios": "^0.21.1",
|
||||
"bootstrap": "^5.0.0-beta3",
|
||||
"mobx": "^6.1.8",
|
||||
"mobx-react": "^7.1.0",
|
||||
"next": "^10.0.9",
|
||||
@ -24,6 +26,7 @@
|
||||
"@babel/plugin-proposal-decorators": "^7.13.5",
|
||||
"@types/node": "^14.14.35",
|
||||
"@types/react": "^17.0.3",
|
||||
"tslib": "^2.1.0",
|
||||
"typescript": "^4.2.3"
|
||||
}
|
||||
}
|
||||
|
65
Next/pages/customer.tsx
Normal file
65
Next/pages/customer.tsx
Normal file
@ -0,0 +1,65 @@
|
||||
import React from 'react';
|
||||
import axios from 'axios';
|
||||
|
||||
import { Layout } from './shared/Layout';
|
||||
|
||||
interface CustomerListItem {
|
||||
customerID: string;
|
||||
name: string;
|
||||
email: string;
|
||||
}
|
||||
function RenderCustomerListItemRows(customers: CustomerListItem[]) {
|
||||
let rows = customers.map(x => {
|
||||
return (
|
||||
<tr>
|
||||
<td>{x.name}</td>
|
||||
<td>{x.email}</td>
|
||||
</tr>
|
||||
);
|
||||
})
|
||||
return (
|
||||
<tbody>
|
||||
{rows}
|
||||
</tbody>
|
||||
)
|
||||
}
|
||||
class Customer extends React.Component<{}, {customer: CustomerListItem[] }> {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
customer: [],
|
||||
}
|
||||
}
|
||||
async componentDidMount() {
|
||||
try {
|
||||
let data = await axios.get<CustomerListItem[]>("http://localhost:5000/api/customer");0
|
||||
console.log(data.data);
|
||||
this.setState({
|
||||
customer: data.data
|
||||
});
|
||||
} catch (error) {
|
||||
this.setState({
|
||||
customer: [],
|
||||
});
|
||||
}
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<table className="table table-hover table-striped table-sm">
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Email</th>
|
||||
</tr>
|
||||
{RenderCustomerListItemRows(this.state.customer)}
|
||||
</table>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default function CustomerPage() {
|
||||
return (
|
||||
<Layout title="Customers">
|
||||
<Customer></Customer>
|
||||
</Layout>
|
||||
)
|
||||
}
|
@ -1,24 +1,37 @@
|
||||
import { makeObservable, observable, action } from 'mobx';
|
||||
import { observer } from 'mobx-react';
|
||||
import React from 'react';
|
||||
import Link from 'next/link';
|
||||
import { Layout } from './shared/Layout';
|
||||
|
||||
@observer class Index extends React.Component {
|
||||
@observable x: number = 0;
|
||||
class Index extends React.Component {
|
||||
x: number = 0;
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
makeObservable(this, {
|
||||
x: observable,
|
||||
clickMe: action,
|
||||
})
|
||||
}
|
||||
|
||||
@action
|
||||
clickMe() {
|
||||
this.x += 1;
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<button onClick={this.clickMe.bind(this)}>{this.x}</button>
|
||||
<Link href="/todo">Go to Todo</Link>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default Index;
|
||||
|
||||
export default function IndexPage() {
|
||||
return (<Layout title="Home">
|
||||
<Index></Index>
|
||||
</Layout>);
|
||||
}
|
79
Next/pages/shared/Layout.tsx
Normal file
79
Next/pages/shared/Layout.tsx
Normal file
@ -0,0 +1,79 @@
|
||||
import React from 'react'
|
||||
import Head from 'next/head'
|
||||
import Link from 'next/link'
|
||||
import { useRouter } from 'next/router'
|
||||
|
||||
import 'bootstrap/dist/css/bootstrap.min.css'
|
||||
|
||||
const NavLink: React.FunctionComponent<{href: string}> = ({href, children}) => {
|
||||
const router = useRouter();
|
||||
const NavLinkClasses = (active) => {
|
||||
if(active) {
|
||||
return "nav-link active";
|
||||
} else {
|
||||
return "nav-link"
|
||||
}
|
||||
}
|
||||
const NavLinkCurrent = (active) => {
|
||||
if(active) {
|
||||
return "page";
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
const active = router.pathname == href;
|
||||
|
||||
return (
|
||||
<li className='nav-item active'>
|
||||
<Link href={href}>
|
||||
<a className={NavLinkClasses(active)} aria-current={NavLinkCurrent}>{children}</a>
|
||||
</Link>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
|
||||
const NavBar: React.FunctionComponent<{}> = () => {
|
||||
return (
|
||||
<nav className='navbar navbar-expand-lg navbar-light bg-light'>
|
||||
<a className='navbar-brand' href='#'>
|
||||
Navbar
|
||||
</a>
|
||||
<button
|
||||
className='navbar-toggler'
|
||||
type='button'
|
||||
data-toggle='collapse'
|
||||
data-target='#navbarSupportedContent'
|
||||
aria-controls='navbarSupportedContent'
|
||||
aria-expanded='false'
|
||||
aria-label='Toggle navigation'
|
||||
>
|
||||
<span className='navbar-toggler-icon' />
|
||||
</button>
|
||||
<div className='collapse navbar-collapse' id='navbarSupportedContent'>
|
||||
<ul className='navbar-nav mr-auto'>
|
||||
<NavLink href="/">Home</NavLink>
|
||||
<NavLink href="/todo">Todo</NavLink>
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
)
|
||||
}
|
||||
export class Layout extends React.Component<{
|
||||
title: string
|
||||
}> {
|
||||
render () {
|
||||
return (
|
||||
<div>
|
||||
<Head>
|
||||
<meta name='viewport' content='width=device-width, initial-scale=1' />
|
||||
<title>{this.props.title} - TestAppRuna</title>
|
||||
</Head>
|
||||
<header>
|
||||
<NavBar />
|
||||
</header>
|
||||
<main>{this.props.children}</main>
|
||||
<footer></footer>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
90
Next/pages/todo.tsx
Normal file
90
Next/pages/todo.tsx
Normal file
@ -0,0 +1,90 @@
|
||||
import React from 'react';
|
||||
import { observable, action, makeObservable } from 'mobx';
|
||||
import { PropTypes } from 'mobx-react';
|
||||
import { Layout } from './shared/Layout';
|
||||
import 'bootstrap/dist/css/bootstrap.min.css';
|
||||
|
||||
interface ToDoListItem {
|
||||
todo: string,
|
||||
checked: boolean,
|
||||
}
|
||||
|
||||
interface ToDoState {
|
||||
inputText: string,
|
||||
toDoList: ToDoListItem[],
|
||||
}
|
||||
|
||||
const TodoItem: React.FunctionComponent<{
|
||||
item: ToDoListItem,
|
||||
onRemoveButtonClick: (id) => void
|
||||
}> = ({ item, onRemoveButtonClick }) => {
|
||||
let onClick = () => {
|
||||
onRemoveButtonClick(item);
|
||||
}
|
||||
return (
|
||||
<div>
|
||||
<button onClick={onClick}>Remove</button>
|
||||
{item.todo}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
const TodoList: React.FunctionComponent<{
|
||||
items: ToDoListItem[],
|
||||
onChange?: (item) => void
|
||||
}> = ({ items, onChange }) => {
|
||||
const onRemoveButtonClick = (item) => {
|
||||
if(onChange) {
|
||||
onChange(item);
|
||||
}
|
||||
}
|
||||
const listItems = items.map(Q => <TodoItem onRemoveButtonClick={onRemoveButtonClick} item={Q}></TodoItem>)
|
||||
return <div>{listItems}</div>;
|
||||
}
|
||||
|
||||
|
||||
class Todo extends React.Component<{},ToDoState> {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
inputText: "",
|
||||
toDoList: [],
|
||||
};
|
||||
}
|
||||
onTextInput = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
this.setState({
|
||||
inputText: e.target.value,
|
||||
})
|
||||
};
|
||||
onChange = (item) => {
|
||||
this.setState(
|
||||
{
|
||||
toDoList: this.state.toDoList.filter(x => item.todo != x.todo)
|
||||
}
|
||||
)
|
||||
};
|
||||
addList = () => {
|
||||
this.setState({
|
||||
inputText: "",
|
||||
toDoList: [...this.state.toDoList, {
|
||||
todo: this.state.inputText, checked: false
|
||||
}],
|
||||
})
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
|
||||
<div>
|
||||
<input value={this.state.inputText} onChange={this.onTextInput}></input>
|
||||
{this.state.inputText}
|
||||
</div>
|
||||
<button onClick={this.addList}>Add</button>
|
||||
<TodoList onChange={this.onChange} items={this.state.toDoList}></TodoList>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default () => {
|
||||
return <Layout title="Todo"><Todo></Todo></Layout>
|
||||
}
|
@ -23,8 +23,7 @@
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"jsx": "preserve",
|
||||
"esModuleInterop": true,
|
||||
"experimentalDecorators": true
|
||||
"esModuleInterop": true
|
||||
},
|
||||
"include": [
|
||||
"next-env.d.ts",
|
||||
|
1895
Next/yarn.lock
Normal file
1895
Next/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
25
TestAppRuna/.vscode/launch.json
vendored
Normal file
25
TestAppRuna/.vscode/launch.json
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": ".NET Core Launch (console)",
|
||||
"type": "coreclr",
|
||||
"request": "launch",
|
||||
"preLaunchTask": "build",
|
||||
"program": "${workspaceFolder}/bin/Debug/net5.0/TestAppRuna.dll",
|
||||
"args": [],
|
||||
"cwd": "${workspaceFolder}",
|
||||
"console": "internalConsole",
|
||||
"stopAtEntry": false
|
||||
},
|
||||
{
|
||||
"name": ".NET Core Attach",
|
||||
"type": "coreclr",
|
||||
"request": "attach",
|
||||
"processId": "${command:pickProcess}"
|
||||
}
|
||||
]
|
||||
}
|
24
TestAppRuna/.vscode/tasks.json
vendored
Normal file
24
TestAppRuna/.vscode/tasks.json
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
{
|
||||
// See https://go.microsoft.com/fwlink/?LinkId=733558
|
||||
// for the documentation about the tasks.json format
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "build",
|
||||
"command": "dotnet",
|
||||
"type": "shell",
|
||||
"args": [
|
||||
"build",
|
||||
// Ask dotnet build to generate full paths for file names.
|
||||
"/property:GenerateFullPaths=true",
|
||||
// Do not generate summary otherwise it leads to duplicate errors in Problems panel
|
||||
"/consoleloggerparameters:NoSummary"
|
||||
],
|
||||
"group": "build",
|
||||
"presentation": {
|
||||
"reveal": "silent"
|
||||
},
|
||||
"problemMatcher": "$msCompile"
|
||||
}
|
||||
]
|
||||
}
|
@ -14,7 +14,6 @@ namespace TestAppRuna.API
|
||||
{
|
||||
[Route("api/[controller]")]
|
||||
[ApiController]
|
||||
[Authorize(AuthenticationSchemes = "nanaoaccounts")]
|
||||
public class CustomerController : ControllerBase
|
||||
{
|
||||
public CustomerController(ShopDbContext shopDbContext)
|
||||
|
@ -51,6 +51,11 @@ namespace TestAppRuna
|
||||
options.Audience = "testappruna";
|
||||
}
|
||||
);
|
||||
services.AddCors(options => {
|
||||
options.AddPolicy("BelajarNextJS", policy => {
|
||||
policy.WithOrigins("http://localhost:3000");
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
||||
@ -85,6 +90,8 @@ namespace TestAppRuna
|
||||
app.UseAuthentication();
|
||||
app.UseAuthorization();
|
||||
|
||||
app.UseCors("BelajarNextJS");
|
||||
|
||||
app.UseEndpoints(endpoints =>
|
||||
{
|
||||
endpoints.MapDefaultControllerRoute();
|
||||
|
@ -6,6 +6,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="5.0.4" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Cors" Version="2.2.0" />
|
||||
<PackageReference Include="NSwag.AspNetCore" Version="13.10.8" />
|
||||
<PackageReference Include="Serilog.AspNetCore" Version="4.0.0" />
|
||||
<PackageReference Include="Serilog.Formatting.Compact" Version="1.1.0" />
|
||||
|
@ -1,5 +0,0 @@
|
||||
{
|
||||
"devDependencies": {
|
||||
"tslib": "^2.1.0"
|
||||
}
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
devDependencies:
|
||||
tslib: 2.1.0
|
||||
lockfileVersion: 5.2
|
||||
packages:
|
||||
/tslib/2.1.0:
|
||||
dev: true
|
||||
resolution:
|
||||
integrity: sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==
|
||||
specifiers:
|
||||
tslib: ^2.1.0
|
Loading…
Reference in New Issue
Block a user