diff --git a/app/Http/Controllers/Controller.php b/app/Http/Controllers/Controller.php
index 03e02a23..0344d9d7 100644
--- a/app/Http/Controllers/Controller.php
+++ b/app/Http/Controllers/Controller.php
@@ -2,6 +2,7 @@
namespace App\Http\Controllers;
+use App\Http\Controllers\Traits\Responds;
use Illuminate\Foundation\Bus\DispatchesJobs;
use Illuminate\Routing\Controller as BaseController;
use Illuminate\Foundation\Validation\ValidatesRequests;
@@ -9,5 +10,5 @@
class Controller extends BaseController
{
- use AuthorizesRequests, DispatchesJobs, ValidatesRequests;
+ use AuthorizesRequests, DispatchesJobs, ValidatesRequests, Responds;
}
diff --git a/app/Http/Controllers/TreetController.php b/app/Http/Controllers/TreetController.php
new file mode 100644
index 00000000..97a0baa9
--- /dev/null
+++ b/app/Http/Controllers/TreetController.php
@@ -0,0 +1,93 @@
+validate($request, [
+ 'limit' => 'nullable|integer|min:6|max:90',
+ ]);
+
+ $limit = $request->limit ?: 10;
+
+ $treets = Treet::select(['id','app_name', 'image_path', 'description', 'url', 'created_at'])
+ ->orderBy('created_at', 'desc')
+ ->limit($limit)
+ ->get();
+
+ $treets->map(function ($treet) {
+ $treet->date = $treet->created_at->format('m/d/y');
+ });
+
+ return $this->success($treets);
+ }
+
+ /**
+ * Show the form for creating a new resource.
+ */
+ public function create(Request $request)
+ {
+ $this->authorize('create', Treet::class);
+
+ $urls = ['Treesnap' => "https://treesnap.org/",
+ 'FlorestaDB' => "https://app.florestadb.org/login",
+ 'HealthyWoods' => "https://healthywoodsapp.org/",
+ 'Avid Deer' => "https://aviddeer.com/",
+ 'Eastern Forest Pests' => "https://easternforestpests.com/"];
+
+
+ $treet = Treet::create([
+ 'app_name' => $request->app_name,
+ 'image_path' => $request->image_path,
+ 'description' => $request->description,
+ 'url' => $urls[$request->app_name]
+ ]);
+
+ return $this->created($treet);
+
+ }
+ /**
+ * Show the form for editing the specified resource.
+ */
+ public function edit(Request $request, Treet $treet)
+ {
+ $urls = ['Treesnap' => "https://treesnap.org/",
+ 'FlorestaDB' => "https://app.florestadb.org/login",
+ 'HealthyWoods' => "https://healthywoodsapp.org/",
+ 'Avid Deer' => "https://aviddeer.com/",
+ 'Eastern Forest Pests' => "https://easternforestpests.com/"];
+
+ $this->authorize('edit', Treet::class);
+
+ $treet = Treet::find($request->id);
+
+ $treet->update([
+ 'app_name' => $request->app_name ?: $treet->app_name,
+ 'image_path' => $request->image_path ?: $treet->image_path,
+ 'description' => $request->description ?: $treet->description,
+ 'url' => $urls[$request->app_name]
+ ]);
+ return $this->success($treet);
+
+ }
+
+ /**
+ * Remove the specified resource from storage.
+ */
+ public function destroy(Request $request, Treet $treet)
+ {
+ $this->authorize('destroy', Treet::class);
+
+ $treet->find($request->id)->delete();
+ return $this->success($treet);
+ }
+
+}
diff --git a/app/Policies/TreetPolicy.php b/app/Policies/TreetPolicy.php
new file mode 100644
index 00000000..74b6ded0
--- /dev/null
+++ b/app/Policies/TreetPolicy.php
@@ -0,0 +1,69 @@
+isAdmin();
+
+ }
+
+ /**
+ * Determine whether the user can update the model.
+ */
+ public function edit(User $user): bool
+ {
+ return $user->isAdmin();
+
+ }
+
+ /**
+ * Determine whether the user can delete the model.
+ */
+ public function destroy(User $user): bool
+ {
+ return $user->isAdmin();
+
+ }
+
+ /**
+ * Determine whether the user can restore the model.
+ */
+ public function restore(User $user, Treet $treet): bool
+ {
+ //
+ }
+
+ /**
+ * Determine whether the user can permanently delete the model.
+ */
+ public function forceDelete(User $user, Treet $treet): bool
+ {
+ //
+ }
+}
diff --git a/app/Treet.php b/app/Treet.php
new file mode 100644
index 00000000..0c4ab888
--- /dev/null
+++ b/app/Treet.php
@@ -0,0 +1,22 @@
+ 'datetime',
+ ];
+}
diff --git a/composer.json b/composer.json
index e05a85a4..f1c87ac7 100755
--- a/composer.json
+++ b/composer.json
@@ -47,6 +47,7 @@
],
"psr-4": {
"App\\": "app/",
+ "Database\\Factories\\": "database/factories/",
"Database\\Seeders\\": "database/seeders/"
}
},
diff --git a/database/factories/ModelFactory.php b/database/factories/ModelFactory.php
index d411c11e..f725ac4b 100644
--- a/database/factories/ModelFactory.php
+++ b/database/factories/ModelFactory.php
@@ -254,3 +254,19 @@ function fuzifyCoorinates($original_latitude, $original_longitude)
'observations_count' => $faker->numberBetween(10, 5000),
];
});
+
+
+$factory->define(\App\Treet::class, function (Faker\Generator $faker) {
+ $appNames = ['Treesnap','FlorestaDB','HealthyWoods', 'Avid Deer', 'Eastern Forest Pests'];
+ $imagePaths = ["../images/logos/treesnap_logo.png","../images/logos/florestadb_logo.png","../images/logos/healthywoods_logo.png","../images/logos/aviddeer_logo.png","../images/logos/efp_logo.png"];
+ $urls = ["https://treesnap.org/","https://app.florestadb.org/login","https://healthywoodsapp.org/","https://aviddeer.com/","https://easternforestpests.com/"];
+ $randApp = array_rand($appNames, 1);
+
+ return [
+ 'app_name' => $appNames[$randApp],
+ 'image_path' => $imagePaths[$randApp],
+ 'url' => $urls[$randApp],
+ 'description' => $faker->sentence()
+ ];
+});
+
diff --git a/database/migrations/2024_05_14_164032_create_treets_table.php b/database/migrations/2024_05_14_164032_create_treets_table.php
new file mode 100644
index 00000000..bdcfee14
--- /dev/null
+++ b/database/migrations/2024_05_14_164032_create_treets_table.php
@@ -0,0 +1,31 @@
+id();
+ $table->text('app_name');
+ $table->text('image_path');
+ $table->text('url');
+ $table->text('description');
+ $table->timestamps();
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ */
+ public function down(): void
+ {
+ Schema::dropIfExists('treets');
+ }
+};
diff --git a/database/seeders/TreetSeeder.php b/database/seeders/TreetSeeder.php
new file mode 100644
index 00000000..87d92039
--- /dev/null
+++ b/database/seeders/TreetSeeder.php
@@ -0,0 +1,22 @@
+create();
+
+ }
+}
diff --git a/public/images/logos/aviddeer_logo.png b/public/images/logos/aviddeer_logo.png
new file mode 100644
index 00000000..dc427179
Binary files /dev/null and b/public/images/logos/aviddeer_logo.png differ
diff --git a/public/images/logos/efp_logo.png b/public/images/logos/efp_logo.png
new file mode 100644
index 00000000..a1dfcfe4
Binary files /dev/null and b/public/images/logos/efp_logo.png differ
diff --git a/public/images/logos/florestadb_logo.png b/public/images/logos/florestadb_logo.png
new file mode 100644
index 00000000..a8db1399
Binary files /dev/null and b/public/images/logos/florestadb_logo.png differ
diff --git a/public/images/logos/healthywoods_logo.png b/public/images/logos/healthywoods_logo.png
new file mode 100644
index 00000000..1ff00514
Binary files /dev/null and b/public/images/logos/healthywoods_logo.png differ
diff --git a/public/images/logos/treesnap_logo.png b/public/images/logos/treesnap_logo.png
new file mode 100755
index 00000000..92612956
Binary files /dev/null and b/public/images/logos/treesnap_logo.png differ
diff --git a/resources/assets/js/components/HomeJumbotron.jsx b/resources/assets/js/components/HomeJumbotron.jsx
index 7faffb8a..5df2586c 100644
--- a/resources/assets/js/components/HomeJumbotron.jsx
+++ b/resources/assets/js/components/HomeJumbotron.jsx
@@ -25,7 +25,7 @@ export default class HomeJumbotron extends Component {
Meet the scientists that use TreeSnap data
- Tutorials available on YouTube
+ Tutorials available on YouTube
diff --git a/resources/assets/js/components/RecentUpdates.jsx b/resources/assets/js/components/RecentUpdates.jsx
new file mode 100644
index 00000000..02419756
--- /dev/null
+++ b/resources/assets/js/components/RecentUpdates.jsx
@@ -0,0 +1,18 @@
+import React, {Component} from 'react'
+
+export default class RecentUpdates extends Component {
+ render() {
+ return (
+
+
+
Recent Updates
+
+
+
The Hemlock survey has been updated to support the Lingering Hemlock Protocol
+
+
+
+
+ )
+ }
+}
diff --git a/resources/assets/js/components/Treet.jsx b/resources/assets/js/components/Treet.jsx
new file mode 100644
index 00000000..4d4a6132
--- /dev/null
+++ b/resources/assets/js/components/Treet.jsx
@@ -0,0 +1,160 @@
+import React, { Component } from 'react'
+
+export default class Treet extends Component {
+ constructor(props) {
+ super(props)
+
+ this.state = {
+ loading: true,
+ isLoggedIn: false,
+ isEditing: false,
+ appNames: ['HealthyWoods', 'Eastern Forest Pests', 'Avid Deer', 'Treesnap', 'FlorestaDB'],
+ appName: '',
+ imagePath: this.props.treet.image_path,
+ description: this.props.treet.description,
+ currentAppName: this.props.treet.app_name,
+ }
+ this.toggle = this.toggle.bind(this)
+ this.onSubmit = this.onSubmit.bind(this)
+ this.renderAppName = this.renderAppName.bind(this)
+ this.handleChangeAppName = this.handleChangeAppName.bind(this)
+ this.handleChangeDescription = this.handleChangeDescription.bind(this)
+
+
+ }
+ componentDidMount() {
+ axios.get('/web/user/status').then(response => {
+ let data = response.data.data
+ this.setState({
+ isLoggedIn: data.logged_in,
+ })
+ }).catch(error => {
+ console.log(error)
+ })
+ }
+ onSubmit(event) {
+ event.preventDefault();
+ this.props.editTreet(this.props.treet.id,event.target.appName.value,this.state.imagePath,event.target.description.value)
+
+ this.toggle()
+ }
+ handleChangeAppName(event) {
+ this.setState({appName: event.target.value});
+ if(event.target.value == "Treesnap"){
+ this.setState({imagePath: "../images/logos/treesnap_logo.png"});
+ }
+ else if(event.target.value == "FlorestaDB"){
+ this.setState({imagePath: "../images/logos/florestadb_logo.png"});
+ }
+ else if(event.target.value == "HealthyWoods"){
+ this.setState({imagePath: "../images/logos/healthywoods_logo.png"});
+ }
+ else if(event.target.value == "Avid Deer"){
+ this.setState({imagePath: "../images/logos/aviddeer_logo.png"});
+ }
+ else if(event.target.value == "Eastern Forest Pests"){
+ this.setState({imagePath: "../images/logos/efp_logo.png"});
+ }
+ }
+ handleChangeDescription(event) {
+ this.setState({description: event.target.value});
+ }
+ toggle() {
+ this.setState(prevState => ({ isEditing: !prevState.isEditing }));
+ }
+ renderAppName(appName) {
+ return
+ }
+ render() {
+ const treet = this.props.treet
+ return (
+
+
+
+ {this.state.isEditing ?
+ <>
+
+
+
+
+
+ >
+ :
+ <>
+
+
{/* end of row */}
+
+
+ {this.state.isLoggedIn ?
+
+
+
+
+ :null}
+ >
+ }
+
+
+
+
+
+ )
+ }
+}
diff --git a/resources/assets/js/components/TwitterFeed.jsx b/resources/assets/js/components/TwitterFeed.jsx
index dc9a8560..3ee9f309 100644
--- a/resources/assets/js/components/TwitterFeed.jsx
+++ b/resources/assets/js/components/TwitterFeed.jsx
@@ -1,24 +1,198 @@
-import React from 'react'
+import React, { Component } from 'react'
+import Treet from './Treet'
+
+export default class TwitterFeed extends Component {
+ constructor(props) {
+ super(props)
+
+ this.state = {
+ isEditing: false,
+ isLoggedIn: false,
+ appNames: ['Eastern Forest Pests','HealthyWoods' , 'Avid Deer', 'Treesnap', 'FlorestaDB'],
+ appName: '',
+ imagePath: '',
+ description: '',
+ treets:[],
+ isOpen: false,
+
+
+ }
+ this.onSubmit = this.onSubmit.bind(this)
+ this.editTreet = this.editTreet.bind(this)
+ this.handleChangeAppName = this.handleChangeAppName.bind(this)
+ this.handleChangeDescription = this.handleChangeDescription.bind(this)
+ this.deleteTreet = this.deleteTreet.bind(this)
+ this.toggle = this.toggle.bind(this)
+
+ }
-export default class TwitterFeed extends React.Component {
componentDidMount() {
- this.interval = setInterval(() => {
- if (window.twttr) {
- window.twttr.widgets.load()
- clearInterval(this.interval)
+ axios.get('/web/user/status').then(response => {
+ let data = response.data.data
+ this.setState({
+ isLoggedIn: data.logged_in,
+ })
+ }).catch(error => {
+ console.log(error)
+ })
+ this.loadTreets()
+
+ setInterval(this.loadTreets.bind(this), 120000)
+ }
+ toggle() {
+ this.setState(prevState => ({ isOpen: !prevState.isOpen }));
+ }
+ loadTreets() {
+ axios.get(`/web/treets/feed`).then(response => {
+ this.setState({treets: response.data.data, loading: false})
+ }).catch(error => {
+ console.log(error)
+ this.setState({loading: false})
+ })
+ }
+ deleteTreet(treet){
+ axios.delete(`/web/treet/${treet.id}`).then(response => {
+ this.loadTreets()
+ }).catch(error => {
+ console.log(error)
+ })
+ }
+ editTreet(id, appName, imagePath, description) {
+ axios.put(`/web/treets/update/${id}`, {
+ app_name : appName,
+ image_path : imagePath,
+ description : description,
+ }).then(response => {
+ this.loadTreets()
+
+ }).catch(error => {
+ if (error.response) {
+ console.log(error)
}
- }, 15)
+ })
+ }
+ onSubmit(event) {
+ event.preventDefault();
+
+ axios.post('/web/treets/create', {
+ app_name : event.target.appName.value,
+ image_path : this.state.imagePath,
+ description : event.target.description.value,
+ }).then(response => {
+ this.loadTreets()
+ this.toggle()
+ this.setState({appName: ''});
+ this.setState({description: ''});
+ cosnole.log(response.data)
+ }).catch(error => {
+ if (error.response) {
+ console.log(error)
+ }
+ })
+ }
+ handleChangeAppName(event) {
+
+ this.setState({appName: event.target.value});
+ if(event.target.value == "Treesnap"){
+ this.setState({imagePath: "../images/logos/treesnap_logo.png"});
+ }
+ else if(event.target.value == "FlorestaDB"){
+ this.setState({imagePath: "../images/logos/florestadb_logo.png"});
+ }
+ else if(event.target.value == "HealthyWoods"){
+ this.setState({imagePath: "../images/logos/healthywoods_logo.png"});
+ }
+ else if(event.target.value == "Avid Deer"){
+ this.setState({imagePath: "../images/logos/aviddeer_logo.png"});
+ }
+ else if(event.target.value == "Eastern Forest Pests"){
+ this.setState({imagePath: "../images/logos/efp_logo.png"});
+ }
+ }
+ handleChangeDescription(event) {
+
+ this.setState({description: event.target.value});
+ }
+ renderButton(isOpen) {
+ let button;
+ if(isOpen){
+ return ;
+ }
+ return ;
}
- render() {
+ renderAppName(appName) {
return (
+
+
+ )
+ }
+
+render() {
+ const treetList = this.state.treets.map((treet)=>( ))
+
+ return (
-
Tweets by Treesnapapp
+ {this.state.isLoggedIn ?
+
+ this.renderButton(this.state.isOpen)
+ :null}
+ {this.state.isOpen ?
+
+ :null}
+
+ {this.state.loading ?
+
+
+
+ : null}
+
+ {treetList}
+ {this.state.treets.length === 0 && !this.state.loading ?
+
There are no treets at this time
+ : null}
+
+
)
}
}
diff --git a/resources/assets/js/scenes/WelcomeScene.jsx b/resources/assets/js/scenes/WelcomeScene.jsx
index aebbd072..d2fce896 100644
--- a/resources/assets/js/scenes/WelcomeScene.jsx
+++ b/resources/assets/js/scenes/WelcomeScene.jsx
@@ -2,30 +2,53 @@ import React from 'react'
import Navbar from '../components/Navbar'
import HomeJumbotron from '../components/HomeJumbotron'
import FeaturesList from '../components/FeaturesList'
+import RecentUpdates from '../components/RecentUpdates'
import HomeFooter from '../components/HomeFooter'
import Leaderboard from '../components/Leaderboard'
import TwitterFeed from '../components/TwitterFeed'
import ObservationFeed from '../components/ObservationsFeed'
import Scene from './Scene'
+import Dropdown from '../components/Dropdown'
+import User from '../helpers/User'
+
+
export default class Welcome extends Scene {
constructor(props) {
super(props)
+ this.state = {
+ isLoggedIn: false,
+ loading: true,
+ }
document.title = 'TreeSnap - Help Our Nation\'s Trees!'
}
+ componentDidMount() {
+ axios.get('/web/user/status').then(response => {
+ let data = response.data.data
+ this.setState({
+ isLoggedIn: data.logged_in,
+ })
+ }).catch(error => {
+ console.log(error)
+ })
+ }
+
+
/**
* Render the scene.
*
* @returns {XML}
*/
+
render() {
return (
+
@@ -40,11 +63,13 @@ export default class Welcome extends Scene {
Latest Observations
- {/*
*/}
- {/*
Tweets
*/}
- {/*
Latest Tweets by @treesnapapp
*/}
- {/*
*/}
- {/*
*/}
+
+
+
Recent Updates
+
+
Latest Updates from Staton Lab
+
+
diff --git a/resources/assets/sass/_hexagon.scss b/resources/assets/sass/_hexagon.scss
index aafde16e..e39b14e6 100644
--- a/resources/assets/sass/_hexagon.scss
+++ b/resources/assets/sass/_hexagon.scss
@@ -8,6 +8,11 @@
strong {
color: $white;
}
+ }
+ &.is-lighter-light {
+ background-color: $primary;
+
+ color: $white;
}
}
diff --git a/resources/assets/sass/_layout.scss b/resources/assets/sass/_layout.scss
index 5246f96f..87193ef9 100644
--- a/resources/assets/sass/_layout.scss
+++ b/resources/assets/sass/_layout.scss
@@ -47,7 +47,7 @@ body,
}
.flex-space-between {
- @include justify-content(space-between);
+ justify-content: space-between;
}
.flex-wrap {
@@ -120,9 +120,6 @@ a.box {
}
}
-.image-gallery-slide-wrapper {
- //background-color: #222;
-}
.image-gallery-slide {
background-color: transparent !important;
@@ -327,27 +324,105 @@ a.box {
}
.disabled {
- justify-content: center;
- padding-bottom: calc(0.5em - 1px);
- padding-left: 1em;
- padding-right: 1em;
- padding-top: calc(0.5em - 1px);
- text-align: center;
- white-space: nowrap;
- flex-direction: row;
+ justify-content: center;
+ padding-bottom: calc(0.5em - 1px);
+ padding-left: 1em;
+ padding-right: 1em;
+ padding-top: calc(0.5em - 1px);
+ text-align: center;
+ white-space: nowrap;
+ flex-direction: row;
}
-.flex{
+.update-row {
+ display: flex;
+ flex-direction: row;
+ justify-content: center;
+ align-items: center;
+}
+
+.recent-updates {
+ padding: 2rem 0;
+ background-color: #289482;
+ // background-color: whitesmoke;
+}
+
+.update-marquee {
+ height: 100%;
+ width: 100%;
display: flex;
+ flex-direction: column;
+ align-items: center;
+}
+
+.update-card {
+ background-color: white;
+ height: 200px;
+ width: 600px;
+ border-radius: 2px;
+ padding: 20px;
}
-.flex-row{
+
+.w-100 {
+ width: 100%;
+}
+
+.recent-updates-form {
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+}
+
+.input-width {
+ min-width: 250px;
+}
+
+.textarea-height-8em {
+ height: 8em;
+}
+
+.textarea-height-4em {
+ height: 4em;
+}
+
+.flex-column-left {
+ display: flex;
+ flex-direction: column;
+ align-items: start;
+}
+
+.flex-column-center {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+}
+
+.flex {
+ display: flex;
+}
+
+.flex-row {
display: flex;
flex-direction: row;
+ align-items: center;
+}
+
+.edit {
+ display: flex;
+ width: 100%;
+ margin-top: 10px;
}
-.flex-col{
+
+.text-white {
+ color: white;
+}
+
+
+.flex-col {
display: flex;
flex-direction: column;
}
-.fit-content{
+
+.fit-content {
width: fit-content;
-}
\ No newline at end of file
+}
diff --git a/routes/web-services.php b/routes/web-services.php
index 7f33a89b..a0c92513 100644
--- a/routes/web-services.php
+++ b/routes/web-services.php
@@ -24,3 +24,11 @@
// Refresh OAuth Tokens
Route::post('/refresh-tokens', 'TokensController@refreshTokens');
});
+Route::group([
+ 'prefix' => 'v1',
+ 'middleware' => ['auth:web-services'],
+], function () {
+ // Treet Routes
+ Route::get('/treets/feed', 'TreetController@index');
+
+});
diff --git a/routes/web.php b/routes/web.php
index f88f6855..ac430c4a 100644
--- a/routes/web.php
+++ b/routes/web.php
@@ -84,8 +84,13 @@
// Public Routes in the Confirmations Controller
Route::get('/web/confirmations/count/{id}', 'PublicConfirmationsController@count');
+Route::get('/web/treets/feed', 'TreetController@index');
+Route::delete('/web/treet/{id}', 'TreetController@destroy');
+Route::post('/web/treets/create', 'TreetController@create');
+Route::put('/web/treets/update/{id}', 'TreetController@edit');
// Authenticated Users Only (could be admin, scientist or user)
Route::group(['middleware' => ['auth']], function () {
+ // Treets
// Observations
Route::delete('/web/observation/{id}', 'ObservationsController@delete');
diff --git a/tests/Feature/ObservationsWebServiceTest.php b/tests/Feature/ObservationsWebServiceTest.php
index 041375cc..13ee904e 100644
--- a/tests/Feature/ObservationsWebServiceTest.php
+++ b/tests/Feature/ObservationsWebServiceTest.php
@@ -4,6 +4,7 @@
use App\Observation;
use App\User;
+use App\Treet;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Laravel\Passport\Passport;
use Tests\TestCase;
@@ -17,7 +18,26 @@ protected function setUp(): void
parent::setUp();
$this->withHeader('Accept', 'application/json');
}
+ public function testGetTreets()
+ {
+ /** @var User $user */
+ $user = factory(User::class)->create();
+ $token = $user->createToken(uniqid());
+
+ factory(Treet::class, 10)->create();
+
+ $this->actingAs($user);
+ $response = $this->withHeader('Authorization', "Bearer $token->accessToken")
+ ->get('/web-services/v1/treets/feed', [
+ 'limit' => 10
+ ]);
+
+ // dd($response->json());
+
+ $response->assertStatus(200);
+ $response->assertJsonStructure($this->getTreetResponseStructure());
+ }
public function testMyObservationsService()
{
/** @var User $user */
@@ -75,6 +95,22 @@ public function testGettingAnObservation()
$response->assertSuccessful();
}
+ protected function getTreetResponseStructure()
+ {
+ return [
+ 'error_code',
+ 'data' => [
+ [
+ 'id',
+ 'app_name',
+ 'description',
+ 'url',
+ 'created_at',
+ 'date',
+ ],
+ ],
+ ];
+ }
protected function getPaginatedObservationResponseStructure()
{
return [
diff --git a/tests/Feature/RecentUpdatesTest.php b/tests/Feature/RecentUpdatesTest.php
new file mode 100644
index 00000000..a87a0df2
--- /dev/null
+++ b/tests/Feature/RecentUpdatesTest.php
@@ -0,0 +1,94 @@
+create([
+ 'id'=>999,
+ 'app_name'=>'test',
+ 'description'=>'testing'
+ ]);
+ $response = $this->get('/web/treets/feed');
+ $response->assertSuccessful();
+ }
+ public function test_edit(): void
+ {
+ //create admin user
+ $user = factory(User::class)->create([
+ 'role_id' => Role::where('name', 'Admin')->first()->id,
+ ]);
+ $this->actingAs($user);
+
+ //create entry
+ factory(Treet::class)->create([
+ 'id'=>999,
+ 'app_name'=>'test',
+ 'description'=>'unchanged'
+ ]);
+ //get description
+ $response = $this->get('/web/treets/feed');
+ $description1 = $response->json()['data'][0]['description'];
+ //edit description
+ $response = $this->put('/web/treets/update/999',[
+ 'description'=>'changed'
+ ]);
+ $description2 = $response->json()['data']['description'];
+
+
+ //compare descriptions
+ $this->assertNotEquals($description1, $description2);
+
+
+ }
+ public function test_create(): void
+ {
+ //create admin user
+ $user = factory(User::class)->create([
+ 'role_id' => Role::where('name', 'Admin')->first()->id,
+ ]);
+ $this->actingAs($user);
+
+ //create entry
+ $response = $this->post('/web/treets/create',[
+ 'app_name' => 'Test Name',
+ 'image_path' => 'ImagePath',
+ 'description' => 'Description'
+ ]);
+
+ $response->assertSuccessful();
+
+ }
+ public function test_destroy(): void
+ {
+ //create admin user
+ $user = factory(User::class)->create([
+ 'role_id' => Role::where('name', 'Admin')->first()->id,
+ ]);
+ $this->actingAs($user);
+
+ //create entry
+ factory(Treet::class)->create([
+ 'id'=>999,
+ 'app_name'=>'test',
+ 'description'=>'testing'
+ ]);
+ //delete entry
+ $response = $this->delete('/web/treet/999');
+
+ //assert that entry has been removed
+ $this->assertFalse(Treet::where('id','999')->exists());
+
+ }
+}